Skip to content

Commit bc69c7e

Browse files
committed
Parse JSDoc types using the normal TS parser
This means that JSDoc types can include the full range of Typescript types now. It also means that Typescript annotations can include JSDoc types. This is disallowed with a new error, however. But Typescript can still give the correct types to JSDoc that shows up in .ts files by mistake. This can easily happen, for example with types like ```ts var x: number? = null; var y: ?string = null; var z: function(string,string): string = (s,t) => s + t; // less likely to show up, but still understood. var ka: ? = 1; ``` In the future, I will add a quick fix to convert these into the correct types. Fixes microsoft#16550
1 parent fd2dd2e commit bc69c7e

8 files changed

Lines changed: 254 additions & 542 deletions

File tree

src/compiler/binder.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,14 @@ namespace ts {
280280
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
281281
const functionType = <JSDocFunctionType>node.parent;
282282
const index = indexOf(functionType.parameters, node);
283-
return "arg" + index as __String;
283+
switch ((node as ParameterDeclaration).type.kind) {
284+
case SyntaxKind.JSDocThisType:
285+
return "this" as __String;
286+
case SyntaxKind.JSDocConstructorType:
287+
return "new" as __String;
288+
default:
289+
return "arg" + index as __String;
290+
}
284291
case SyntaxKind.JSDocTypedefTag:
285292
const parentNode = node.parent && node.parent.parent;
286293
let nameFromParentNode: __String;
@@ -1395,14 +1402,12 @@ namespace ts {
13951402
case SyntaxKind.ObjectLiteralExpression:
13961403
case SyntaxKind.TypeLiteral:
13971404
case SyntaxKind.JSDocTypeLiteral:
1398-
case SyntaxKind.JSDocRecordType:
13991405
case SyntaxKind.JsxAttributes:
14001406
return ContainerFlags.IsContainer;
14011407

14021408
case SyntaxKind.InterfaceDeclaration:
14031409
return ContainerFlags.IsContainer | ContainerFlags.IsInterface;
14041410

1405-
case SyntaxKind.JSDocFunctionType:
14061411
case SyntaxKind.ModuleDeclaration:
14071412
case SyntaxKind.TypeAliasDeclaration:
14081413
case SyntaxKind.MappedType:
@@ -1422,9 +1427,10 @@ namespace ts {
14221427
case SyntaxKind.GetAccessor:
14231428
case SyntaxKind.SetAccessor:
14241429
case SyntaxKind.CallSignature:
1430+
case SyntaxKind.JSDocFunctionType:
1431+
case SyntaxKind.FunctionType:
14251432
case SyntaxKind.ConstructSignature:
14261433
case SyntaxKind.IndexSignature:
1427-
case SyntaxKind.FunctionType:
14281434
case SyntaxKind.ConstructorType:
14291435
return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike;
14301436

@@ -1502,7 +1508,6 @@ namespace ts {
15021508
case SyntaxKind.TypeLiteral:
15031509
case SyntaxKind.ObjectLiteralExpression:
15041510
case SyntaxKind.InterfaceDeclaration:
1505-
case SyntaxKind.JSDocRecordType:
15061511
case SyntaxKind.JSDocTypeLiteral:
15071512
case SyntaxKind.JsxAttributes:
15081513
// Interface/Object-types always have their children added to the 'members' of
@@ -2095,6 +2100,7 @@ namespace ts {
20952100
case SyntaxKind.SetAccessor:
20962101
return bindPropertyOrMethodOrAccessor(<Declaration>node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
20972102
case SyntaxKind.FunctionType:
2103+
case SyntaxKind.JSDocFunctionType:
20982104
case SyntaxKind.ConstructorType:
20992105
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
21002106
case SyntaxKind.TypeLiteral:
@@ -2157,18 +2163,13 @@ namespace ts {
21572163
case SyntaxKind.ModuleBlock:
21582164
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
21592165

2160-
case SyntaxKind.JSDocRecordMember:
2161-
return bindPropertyWorker(node as JSDocRecordMember);
21622166
case SyntaxKind.JSDocPropertyTag:
21632167
return declareSymbolAndAddToSymbolTable(node as JSDocPropertyTag,
21642168
(node as JSDocPropertyTag).isBracketed || ((node as JSDocPropertyTag).typeExpression && (node as JSDocPropertyTag).typeExpression.type.kind === SyntaxKind.JSDocOptionalType) ?
21652169
SymbolFlags.Property | SymbolFlags.Optional : SymbolFlags.Property,
21662170
SymbolFlags.PropertyExcludes);
2167-
case SyntaxKind.JSDocFunctionType:
2168-
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
21692171
case SyntaxKind.JSDocTypeLiteral:
2170-
case SyntaxKind.JSDocRecordType:
2171-
return bindAnonymousTypeWorker(node as JSDocTypeLiteral | JSDocRecordType);
2172+
return bindAnonymousTypeWorker(node as JSDocTypeLiteral);
21722173
case SyntaxKind.JSDocTypedefTag: {
21732174
const { fullName } = node as JSDocTypedefTag;
21742175
if (!fullName || fullName.kind === SyntaxKind.Identifier) {
@@ -2183,7 +2184,7 @@ namespace ts {
21832184
return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes);
21842185
}
21852186

2186-
function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral | JSDocRecordType) {
2187+
function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) {
21872188
return bindAnonymousDeclaration(<Declaration>node, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
21882189
}
21892190

src/compiler/checker.ts

Lines changed: 67 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6339,7 +6339,7 @@ namespace ts {
63396339
const resolvedSymbol = resolveName(param, paramSymbol.name, SymbolFlags.Value, undefined, undefined);
63406340
paramSymbol = resolvedSymbol;
63416341
}
6342-
if (i === 0 && paramSymbol.name === "this") {
6342+
if (i === 0 && paramSymbol.name === "this" || (param.type && param.type.kind === SyntaxKind.JSDocThisType)) {
63436343
hasThisParameter = true;
63446344
thisParameter = param.symbol;
63456345
}
@@ -6798,16 +6798,13 @@ namespace ts {
67986798
switch (node.kind) {
67996799
case SyntaxKind.TypeReference:
68006800
return (<TypeReferenceNode>node).typeName;
6801-
case SyntaxKind.JSDocTypeReference:
6802-
return (<JSDocTypeReference>node).name;
68036801
case SyntaxKind.ExpressionWithTypeArguments:
68046802
// We only support expressions that are simple qualified names. For other
68056803
// expressions this produces undefined.
68066804
const expr = (<ExpressionWithTypeArguments>node).expression;
68076805
if (isEntityNameExpression(expr)) {
68086806
return expr;
68096807
}
6810-
68116808
// fall through;
68126809
}
68136810

@@ -6833,8 +6830,8 @@ namespace ts {
68336830
return type;
68346831
}
68356832

6836-
if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) {
6837-
// A JSDocTypeReference may have resolved to a value (as opposed to a type). If
6833+
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
6834+
// A jsdoc TypeReference may have resolved to a value (as opposed to a type). If
68386835
// the symbol is a constructor function, return the inferred class type; otherwise,
68396836
// the type of this reference is just the type of the value we resolved to.
68406837
const valueType = getTypeOfSymbol(symbol);
@@ -6862,14 +6859,16 @@ namespace ts {
68626859
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
68636860
}
68646861

6865-
if (symbol.flags & SymbolFlags.Function && node.kind === SyntaxKind.JSDocTypeReference && (symbol.members || getJSDocClassTag(symbol.valueDeclaration))) {
6862+
if (symbol.flags & SymbolFlags.Function &&
6863+
isJSDocTypeReference(node) &&
6864+
(symbol.members || getJSDocClassTag(symbol.valueDeclaration))) {
68666865
return getInferredClassType(symbol);
68676866
}
68686867
}
68696868

6870-
function getPrimitiveTypeFromJSDocTypeReference(node: JSDocTypeReference): Type {
6871-
if (isIdentifier(node.name)) {
6872-
switch (node.name.text) {
6869+
function getPrimitiveTypeFromJSDocTypeReference(node: TypeReferenceNode): Type {
6870+
if (isIdentifier(node.typeName)) {
6871+
switch (node.typeName.text) {
68736872
case "String":
68746873
return stringType;
68756874
case "Number":
@@ -6909,7 +6908,7 @@ namespace ts {
69096908
let symbol: Symbol;
69106909
let type: Type;
69116910
let meaning = SymbolFlags.Type;
6912-
if (node.kind === SyntaxKind.JSDocTypeReference) {
6911+
if (isJSDocTypeReference(node)) {
69136912
type = getPrimitiveTypeFromJSDocTypeReference(node);
69146913
meaning |= SymbolFlags.Value;
69156914
}
@@ -7799,15 +7798,6 @@ namespace ts {
77997798
return links.resolvedType;
78007799
}
78017800

7802-
function getTypeFromJSDocTupleType(node: JSDocTupleType): Type {
7803-
const links = getNodeLinks(node);
7804-
if (!links.resolvedType) {
7805-
const types = map(node.types, getTypeFromTypeNode);
7806-
links.resolvedType = createTupleType(types);
7807-
}
7808-
return links.resolvedType;
7809-
}
7810-
78117801
function getThisType(node: Node): Type {
78127802
const container = getThisContainer(node, /*includeArrowFunctions*/ false);
78137803
const parent = container && container.parent;
@@ -7852,16 +7842,18 @@ namespace ts {
78527842
case SyntaxKind.NeverKeyword:
78537843
return neverType;
78547844
case SyntaxKind.ObjectKeyword:
7855-
return nonPrimitiveType;
7845+
if (node.flags & NodeFlags.JavaScriptFile) {
7846+
return anyType;
7847+
}
7848+
else {
7849+
return nonPrimitiveType;
7850+
}
78567851
case SyntaxKind.ThisType:
78577852
case SyntaxKind.ThisKeyword:
78587853
return getTypeFromThisTypeNode(node);
78597854
case SyntaxKind.LiteralType:
78607855
return getTypeFromLiteralTypeNode(<LiteralTypeNode>node);
7861-
case SyntaxKind.JSDocLiteralType:
7862-
return getTypeFromLiteralTypeNode((<JSDocLiteralType>node).literal);
78637856
case SyntaxKind.TypeReference:
7864-
case SyntaxKind.JSDocTypeReference:
78657857
return getTypeFromTypeReference(<TypeReferenceNode>node);
78667858
case SyntaxKind.TypePredicate:
78677859
return booleanType;
@@ -7870,12 +7862,10 @@ namespace ts {
78707862
case SyntaxKind.TypeQuery:
78717863
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
78727864
case SyntaxKind.ArrayType:
7873-
case SyntaxKind.JSDocArrayType:
78747865
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
78757866
case SyntaxKind.TupleType:
78767867
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
78777868
case SyntaxKind.UnionType:
7878-
case SyntaxKind.JSDocUnionType:
78797869
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
78807870
case SyntaxKind.IntersectionType:
78817871
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node);
@@ -7887,8 +7877,6 @@ namespace ts {
78877877
case SyntaxKind.JSDocThisType:
78887878
case SyntaxKind.JSDocOptionalType:
78897879
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>node).type);
7890-
case SyntaxKind.JSDocRecordType:
7891-
return getTypeFromTypeNode((node as JSDocRecordType).literal);
78927880
case SyntaxKind.FunctionType:
78937881
case SyntaxKind.ConstructorType:
78947882
case SyntaxKind.TypeLiteral:
@@ -7907,8 +7895,6 @@ namespace ts {
79077895
case SyntaxKind.QualifiedName:
79087896
const symbol = getSymbolAtLocation(node);
79097897
return symbol && getDeclaredTypeOfSymbol(symbol);
7910-
case SyntaxKind.JSDocTupleType:
7911-
return getTypeFromJSDocTupleType(<JSDocTupleType>node);
79127898
case SyntaxKind.JSDocVariadicType:
79137899
return getTypeFromJSDocVariadicType(<JSDocVariadicType>node);
79147900
default:
@@ -18474,6 +18460,9 @@ namespace ts {
1847418460

1847518461
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
1847618462
checkGrammarTypeArguments(node, node.typeArguments);
18463+
if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDot && !isInJavaScriptFile(node) && !findAncestor(node, n => n.kind === SyntaxKind.JSDocTypeExpression)) {
18464+
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_used_inside_documentation_comments);
18465+
}
1847718466
const type = getTypeFromTypeReference(node);
1847818467
if (type !== unknownType) {
1847918468
if (node.typeArguments) {
@@ -19372,7 +19361,17 @@ namespace ts {
1937219361
}
1937319362
}
1937419363

19364+
function checkJsDoc(node: FunctionDeclaration | MethodDeclaration) {
19365+
if (!node.jsDoc) {
19366+
return;
19367+
}
19368+
for (const doc of node.jsDoc) {
19369+
checkSourceElement(doc);
19370+
}
19371+
}
19372+
1937519373
function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void {
19374+
checkJsDoc(node);
1937619375
checkDecorators(node);
1937719376
checkSignatureDeclaration(node);
1937819377
const functionFlags = getFunctionFlags(node);
@@ -21971,6 +21970,22 @@ namespace ts {
2197121970
}
2197221971
}
2197321972

21973+
function checkJSDocComment(node: JSDoc) {
21974+
if (isInJavaScriptFile(node) && (node as JSDoc).tags) {
21975+
for (const tag of (node as JSDoc).tags) {
21976+
checkSourceElement(tag);
21977+
}
21978+
}
21979+
}
21980+
21981+
function checkJSDocFunctionType(node: JSDocFunctionType) {
21982+
for (const p of node.parameters) {
21983+
// don't bother with normal parameter checking since jsdoc function parameters only consist of a type
21984+
checkSourceElement(p.type);
21985+
}
21986+
checkSourceElement(node.type);
21987+
}
21988+
2197421989
function checkSourceElement(node: Node): void {
2197521990
if (!node) {
2197621991
return;
@@ -22030,6 +22045,26 @@ namespace ts {
2203022045
case SyntaxKind.ParenthesizedType:
2203122046
case SyntaxKind.TypeOperator:
2203222047
return checkSourceElement((<ParenthesizedTypeNode | TypeOperatorNode>node).type);
22048+
case SyntaxKind.JSDocComment:
22049+
return checkJSDocComment(node as JSDoc);
22050+
case SyntaxKind.JSDocParameterTag:
22051+
return checkSourceElement((node as JSDocParameterTag).typeExpression);
22052+
case SyntaxKind.JSDocFunctionType:
22053+
checkJSDocFunctionType(node as JSDocFunctionType);
22054+
// falls through
22055+
case SyntaxKind.JSDocConstructorType:
22056+
case SyntaxKind.JSDocThisType:
22057+
case SyntaxKind.JSDocVariadicType:
22058+
case SyntaxKind.JSDocNonNullableType:
22059+
case SyntaxKind.JSDocNullableType:
22060+
case SyntaxKind.JSDocAllType:
22061+
case SyntaxKind.JSDocUnknownType:
22062+
if (!isInJavaScriptFile(node) && !findAncestor(node, n => n.kind === SyntaxKind.JSDocTypeExpression)) {
22063+
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_used_inside_documentation_comments);
22064+
}
22065+
return;
22066+
case SyntaxKind.JSDocTypeExpression:
22067+
return checkSourceElement((node as JSDocTypeExpression).type);
2203322068
case SyntaxKind.IndexedAccessType:
2203422069
return checkIndexedAccessType(<IndexedAccessTypeNode>node);
2203522070
case SyntaxKind.MappedType:
@@ -22386,7 +22421,7 @@ namespace ts {
2238622421
node = node.parent;
2238722422
}
2238822423

22389-
return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ;
22424+
return node.parent && node.parent.kind === SyntaxKind.TypeReference ;
2239022425
}
2239122426

2239222427
function isHeritageClauseElementIdentifier(entityName: Node): boolean {
@@ -22540,7 +22575,7 @@ namespace ts {
2254022575
}
2254122576
}
2254222577
else if (isTypeReferenceIdentifier(<EntityName>entityName)) {
22543-
const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace;
22578+
const meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace;
2254422579
return resolveEntityName(<EntityName>entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
2254522580
}
2254622581
else if (entityName.parent.kind === SyntaxKind.JsxAttribute) {

src/compiler/diagnosticMessages.json

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,6 +3463,22 @@
34633463
"category": "Error",
34643464
"code": 8016
34653465
},
3466+
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
3467+
"category": "Error",
3468+
"code": 8017
3469+
},
3470+
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
3471+
"category": "Error",
3472+
"code": 8018
3473+
},
3474+
"Report errors in .js files.": {
3475+
"category": "Message",
3476+
"code": 8019
3477+
},
3478+
"JSDoc types can only used inside documentation comments.": {
3479+
"category": "Error",
3480+
"code": 8020
3481+
},
34663482
"Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": {
34673483
"category": "Error",
34683484
"code": 9002
@@ -3645,18 +3661,5 @@
36453661
"Convert function '{0}' to class": {
36463662
"category": "Message",
36473663
"code": 95002
3648-
},
3649-
3650-
"Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": {
3651-
"category": "Error",
3652-
"code": 8017
3653-
},
3654-
"Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": {
3655-
"category": "Error",
3656-
"code": 8018
3657-
},
3658-
"Report errors in .js files.": {
3659-
"category": "Message",
3660-
"code": 8019
36613664
}
36623665
}

0 commit comments

Comments
 (0)