diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e7b759ff64f..0c3f1a2444d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -280,7 +280,14 @@ namespace ts { Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType); const functionType = node.parent; const index = indexOf(functionType.parameters, node); - return "arg" + index as __String; + switch ((node as ParameterDeclaration).type.kind) { + case SyntaxKind.JSDocThisType: + return "this" as __String; + case SyntaxKind.JSDocConstructorType: + return "new" as __String; + default: + return "arg" + index as __String; + } case SyntaxKind.JSDocTypedefTag: const parentNode = node.parent && node.parent.parent; let nameFromParentNode: __String; @@ -1395,14 +1402,12 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocRecordType: case SyntaxKind.JsxAttributes: return ContainerFlags.IsContainer; case SyntaxKind.InterfaceDeclaration: return ContainerFlags.IsContainer | ContainerFlags.IsInterface; - case SyntaxKind.JSDocFunctionType: case SyntaxKind.ModuleDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.MappedType: @@ -1422,9 +1427,10 @@ namespace ts { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.CallSignature: + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.FunctionType: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; @@ -1502,7 +1508,6 @@ namespace ts { case SyntaxKind.TypeLiteral: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.JSDocRecordType: case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JsxAttributes: // Interface/Object-types always have their children added to the 'members' of @@ -2095,6 +2100,7 @@ namespace ts { case SyntaxKind.SetAccessor: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes); case SyntaxKind.FunctionType: + case SyntaxKind.JSDocFunctionType: case SyntaxKind.ConstructorType: return bindFunctionOrConstructorType(node); case SyntaxKind.TypeLiteral: @@ -2157,18 +2163,13 @@ namespace ts { case SyntaxKind.ModuleBlock: return updateStrictModeStatementList((node).statements); - case SyntaxKind.JSDocRecordMember: - return bindPropertyWorker(node as JSDocRecordMember); case SyntaxKind.JSDocPropertyTag: return declareSymbolAndAddToSymbolTable(node as JSDocPropertyTag, (node as JSDocPropertyTag).isBracketed || ((node as JSDocPropertyTag).typeExpression && (node as JSDocPropertyTag).typeExpression.type.kind === SyntaxKind.JSDocOptionalType) ? SymbolFlags.Property | SymbolFlags.Optional : SymbolFlags.Property, SymbolFlags.PropertyExcludes); - case SyntaxKind.JSDocFunctionType: - return bindFunctionOrConstructorType(node); case SyntaxKind.JSDocTypeLiteral: - case SyntaxKind.JSDocRecordType: - return bindAnonymousTypeWorker(node as JSDocTypeLiteral | JSDocRecordType); + return bindAnonymousTypeWorker(node as JSDocTypeLiteral); case SyntaxKind.JSDocTypedefTag: { const { fullName } = node as JSDocTypedefTag; if (!fullName || fullName.kind === SyntaxKind.Identifier) { @@ -2183,7 +2184,7 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | (node.questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); } - function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral | JSDocRecordType) { + function bindAnonymousTypeWorker(node: TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral) { return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, InternalSymbolName.Type); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6c5fcc36e4d..cd1fbcc379b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6339,7 +6339,7 @@ namespace ts { const resolvedSymbol = resolveName(param, paramSymbol.name, SymbolFlags.Value, undefined, undefined); paramSymbol = resolvedSymbol; } - if (i === 0 && paramSymbol.name === "this") { + if (i === 0 && paramSymbol.name === "this" || (param.type && param.type.kind === SyntaxKind.JSDocThisType)) { hasThisParameter = true; thisParameter = param.symbol; } @@ -6798,8 +6798,6 @@ namespace ts { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; - case SyntaxKind.JSDocTypeReference: - return (node).name; case SyntaxKind.ExpressionWithTypeArguments: // We only support expressions that are simple qualified names. For other // expressions this produces undefined. @@ -6807,7 +6805,6 @@ namespace ts { if (isEntityNameExpression(expr)) { return expr; } - // fall through; } @@ -6833,8 +6830,8 @@ namespace ts { return type; } - if (symbol.flags & SymbolFlags.Value && node.kind === SyntaxKind.JSDocTypeReference) { - // A JSDocTypeReference may have resolved to a value (as opposed to a type). If + if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) { + // A jsdoc TypeReference may have resolved to a value (as opposed to a type). If // the symbol is a constructor function, return the inferred class type; otherwise, // the type of this reference is just the type of the value we resolved to. const valueType = getTypeOfSymbol(symbol); @@ -6862,14 +6859,16 @@ namespace ts { return getTypeFromTypeAliasReference(node, symbol, typeArguments); } - if (symbol.flags & SymbolFlags.Function && node.kind === SyntaxKind.JSDocTypeReference && (symbol.members || getJSDocClassTag(symbol.valueDeclaration))) { + if (symbol.flags & SymbolFlags.Function && + isJSDocTypeReference(node) && + (symbol.members || getJSDocClassTag(symbol.valueDeclaration))) { return getInferredClassType(symbol); } } - function getPrimitiveTypeFromJSDocTypeReference(node: JSDocTypeReference): Type { - if (isIdentifier(node.name)) { - switch (node.name.text) { + function getPrimitiveTypeFromJSDocTypeReference(node: TypeReferenceNode): Type { + if (isIdentifier(node.typeName)) { + switch (node.typeName.text) { case "String": return stringType; case "Number": @@ -6909,7 +6908,7 @@ namespace ts { let symbol: Symbol; let type: Type; let meaning = SymbolFlags.Type; - if (node.kind === SyntaxKind.JSDocTypeReference) { + if (isJSDocTypeReference(node)) { type = getPrimitiveTypeFromJSDocTypeReference(node); meaning |= SymbolFlags.Value; } @@ -7799,15 +7798,6 @@ namespace ts { return links.resolvedType; } - function getTypeFromJSDocTupleType(node: JSDocTupleType): Type { - const links = getNodeLinks(node); - if (!links.resolvedType) { - const types = map(node.types, getTypeFromTypeNode); - links.resolvedType = createTupleType(types); - } - return links.resolvedType; - } - function getThisType(node: Node): Type { const container = getThisContainer(node, /*includeArrowFunctions*/ false); const parent = container && container.parent; @@ -7852,16 +7842,18 @@ namespace ts { case SyntaxKind.NeverKeyword: return neverType; case SyntaxKind.ObjectKeyword: - return nonPrimitiveType; + if (node.flags & NodeFlags.JavaScriptFile) { + return anyType; + } + else { + return nonPrimitiveType; + } case SyntaxKind.ThisType: case SyntaxKind.ThisKeyword: return getTypeFromThisTypeNode(node); case SyntaxKind.LiteralType: return getTypeFromLiteralTypeNode(node); - case SyntaxKind.JSDocLiteralType: - return getTypeFromLiteralTypeNode((node).literal); case SyntaxKind.TypeReference: - case SyntaxKind.JSDocTypeReference: return getTypeFromTypeReference(node); case SyntaxKind.TypePredicate: return booleanType; @@ -7870,12 +7862,10 @@ namespace ts { case SyntaxKind.TypeQuery: return getTypeFromTypeQueryNode(node); case SyntaxKind.ArrayType: - case SyntaxKind.JSDocArrayType: return getTypeFromArrayTypeNode(node); case SyntaxKind.TupleType: return getTypeFromTupleTypeNode(node); case SyntaxKind.UnionType: - case SyntaxKind.JSDocUnionType: return getTypeFromUnionTypeNode(node); case SyntaxKind.IntersectionType: return getTypeFromIntersectionTypeNode(node); @@ -7887,8 +7877,6 @@ namespace ts { case SyntaxKind.JSDocThisType: case SyntaxKind.JSDocOptionalType: return getTypeFromTypeNode((node).type); - case SyntaxKind.JSDocRecordType: - return getTypeFromTypeNode((node as JSDocRecordType).literal); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: @@ -7907,8 +7895,6 @@ namespace ts { case SyntaxKind.QualifiedName: const symbol = getSymbolAtLocation(node); return symbol && getDeclaredTypeOfSymbol(symbol); - case SyntaxKind.JSDocTupleType: - return getTypeFromJSDocTupleType(node); case SyntaxKind.JSDocVariadicType: return getTypeFromJSDocVariadicType(node); default: @@ -18474,6 +18460,9 @@ namespace ts { function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) { checkGrammarTypeArguments(node, node.typeArguments); + if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDot && !isInJavaScriptFile(node) && !findAncestor(node, n => n.kind === SyntaxKind.JSDocTypeExpression)) { + grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_used_inside_documentation_comments); + } const type = getTypeFromTypeReference(node); if (type !== unknownType) { if (node.typeArguments) { @@ -19372,7 +19361,17 @@ namespace ts { } } + function checkJsDoc(node: FunctionDeclaration | MethodDeclaration) { + if (!node.jsDoc) { + return; + } + for (const doc of node.jsDoc) { + checkSourceElement(doc); + } + } + function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void { + checkJsDoc(node); checkDecorators(node); checkSignatureDeclaration(node); const functionFlags = getFunctionFlags(node); @@ -21971,6 +21970,22 @@ namespace ts { } } + function checkJSDocComment(node: JSDoc) { + if (isInJavaScriptFile(node) && (node as JSDoc).tags) { + for (const tag of (node as JSDoc).tags) { + checkSourceElement(tag); + } + } + } + + function checkJSDocFunctionType(node: JSDocFunctionType) { + for (const p of node.parameters) { + // don't bother with normal parameter checking since jsdoc function parameters only consist of a type + checkSourceElement(p.type); + } + checkSourceElement(node.type); + } + function checkSourceElement(node: Node): void { if (!node) { return; @@ -22030,6 +22045,26 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return checkSourceElement((node).type); + case SyntaxKind.JSDocComment: + return checkJSDocComment(node as JSDoc); + case SyntaxKind.JSDocParameterTag: + return checkSourceElement((node as JSDocParameterTag).typeExpression); + case SyntaxKind.JSDocFunctionType: + checkJSDocFunctionType(node as JSDocFunctionType); + // falls through + case SyntaxKind.JSDocConstructorType: + case SyntaxKind.JSDocThisType: + case SyntaxKind.JSDocVariadicType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + if (!isInJavaScriptFile(node) && !findAncestor(node, n => n.kind === SyntaxKind.JSDocTypeExpression)) { + grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_used_inside_documentation_comments); + } + return; + case SyntaxKind.JSDocTypeExpression: + return checkSourceElement((node as JSDocTypeExpression).type); case SyntaxKind.IndexedAccessType: return checkIndexedAccessType(node); case SyntaxKind.MappedType: @@ -22386,7 +22421,7 @@ namespace ts { node = node.parent; } - return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ; + return node.parent && node.parent.kind === SyntaxKind.TypeReference ; } function isHeritageClauseElementIdentifier(entityName: Node): boolean { @@ -22540,7 +22575,7 @@ namespace ts { } } else if (isTypeReferenceIdentifier(entityName)) { - const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace; + const meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; return resolveEntityName(entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); } else if (entityName.parent.kind === SyntaxKind.JsxAttribute) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cf198ec06ec..cee632634a9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3463,6 +3463,22 @@ "category": "Error", "code": 8016 }, + "Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": { + "category": "Error", + "code": 8017 + }, + "Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": { + "category": "Error", + "code": 8018 + }, + "Report errors in .js files.": { + "category": "Message", + "code": 8019 + }, + "JSDoc types can only used inside documentation comments.": { + "category": "Error", + "code": 8020 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 @@ -3645,18 +3661,5 @@ "Convert function '{0}' to class": { "category": "Message", "code": 95002 - }, - - "Octal literal types must use ES2015 syntax. Use the syntax '{0}'.": { - "category": "Error", - "code": 8017 - }, - "Octal literals are not allowed in enums members initializer. Use the syntax '{0}'.": { - "category": "Error", - "code": 8018 - }, - "Report errors in .js files.": { - "category": "Message", - "code": 8019 } } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 089bff53a05..41abbf59305 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -9,6 +9,7 @@ namespace ts { Type = 1 << 2, RequireCompleteParameterList = 1 << 3, IgnoreMissingOpenBrace = 1 << 4, + JSDoc = 1 << 5, } let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node; @@ -392,21 +393,10 @@ namespace ts { case SyntaxKind.JSDocTypeExpression: return visitNode(cbNode, (node).type); - case SyntaxKind.JSDocUnionType: - return visitNodes(cbNode, cbNodes, (node).types); - case SyntaxKind.JSDocTupleType: - return visitNodes(cbNode, cbNodes, (node).types); - case SyntaxKind.JSDocArrayType: - return visitNode(cbNode, (node).elementType); case SyntaxKind.JSDocNonNullableType: return visitNode(cbNode, (node).type); case SyntaxKind.JSDocNullableType: return visitNode(cbNode, (node).type); - case SyntaxKind.JSDocRecordType: - return visitNode(cbNode, (node).literal); - case SyntaxKind.JSDocTypeReference: - return visitNode(cbNode, (node).name) || - visitNodes(cbNode, cbNodes, (node).typeArguments); case SyntaxKind.JSDocOptionalType: return visitNode(cbNode, (node).type); case SyntaxKind.JSDocFunctionType: @@ -418,9 +408,6 @@ namespace ts { return visitNode(cbNode, (node).type); case SyntaxKind.JSDocThisType: return visitNode(cbNode, (node).type); - case SyntaxKind.JSDocRecordMember: - return visitNode(cbNode, (node).name) || - visitNode(cbNode, (node).type); case SyntaxKind.JSDocComment: return visitNodes(cbNode, cbNodes, (node).tags); case SyntaxKind.JSDocParameterTag: @@ -447,8 +434,6 @@ namespace ts { visitNode(cbNode, (node).name); case SyntaxKind.PartiallyEmittedExpression: return visitNode(cbNode, (node).expression); - case SyntaxKind.JSDocLiteralType: - return visitNode(cbNode, (node).literal); } } @@ -1239,14 +1224,6 @@ namespace ts { return parsePropertyNameWorker(/*allowComputedPropertyNames*/ true); } - function parseSimplePropertyName(): Identifier | LiteralExpression { - return parsePropertyNameWorker(/*allowComputedPropertyNames*/ false); - } - - function isSimplePropertyName() { - return token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral || tokenIsIdentifierOrKeyword(token()); - } - function parseComputedPropertyName(): ComputedPropertyName { // PropertyName [Yield]: // LiteralPropertyName @@ -1394,12 +1371,6 @@ namespace ts { return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken; case ParsingContext.JsxChildren: return true; - case ParsingContext.JSDocFunctionParameters: - case ParsingContext.JSDocTypeArguments: - case ParsingContext.JSDocTupleTypes: - return JSDocParser.isJSDocType(); - case ParsingContext.JSDocRecordMembers: - return isSimplePropertyName(); } Debug.fail("Non-exhaustive case in 'isListElement'."); @@ -1494,14 +1465,6 @@ namespace ts { return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.SlashToken; case ParsingContext.JsxChildren: return token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsSlash); - case ParsingContext.JSDocFunctionParameters: - return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.ColonToken || token() === SyntaxKind.CloseBraceToken; - case ParsingContext.JSDocTypeArguments: - return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.CloseBraceToken; - case ParsingContext.JSDocTupleTypes: - return token() === SyntaxKind.CloseBracketToken || token() === SyntaxKind.CloseBraceToken; - case ParsingContext.JSDocRecordMembers: - return token() === SyntaxKind.CloseBraceToken; } } @@ -1887,10 +1850,6 @@ namespace ts { case ParsingContext.ImportOrExportSpecifiers: return Diagnostics.Identifier_expected; case ParsingContext.JsxAttributes: return Diagnostics.Identifier_expected; case ParsingContext.JsxChildren: return Diagnostics.Identifier_expected; - case ParsingContext.JSDocFunctionParameters: return Diagnostics.Parameter_declaration_expected; - case ParsingContext.JSDocTypeArguments: return Diagnostics.Type_argument_expected; - case ParsingContext.JSDocTupleTypes: return Diagnostics.Type_expected; - case ParsingContext.JSDocRecordMembers: return Diagnostics.Property_assignment_expected; } } @@ -1969,9 +1928,14 @@ namespace ts { // The allowReservedWords parameter controls whether reserved words are permitted after the first dot function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { - let entity: EntityName = parseIdentifier(diagnosticMessage); + let entity: EntityName = allowReservedWords ? parseIdentifierName() : parseIdentifier(diagnosticMessage); while (parseOptional(SyntaxKind.DotToken)) { - const node: QualifiedName = createNode(SyntaxKind.QualifiedName, entity.pos); // !!! + if (token() === SyntaxKind.LessThanToken) { + // the entity is part of a JSDoc-style generic, so record this for later in case it's an error + entity.jsdocDot = true; + break; + } + const node: QualifiedName = createNode(SyntaxKind.QualifiedName, entity.pos); node.left = entity; node.right = parseRightSideOfDot(allowReservedWords); entity = finishNode(node); @@ -2098,7 +2062,7 @@ namespace ts { function parseTypeReference(): TypeReferenceNode { const node = createNode(SyntaxKind.TypeReference); - node.typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); + node.typeName = parseEntityName(/*allowReservedWords*/ !!(contextFlags & NodeFlags.JSDoc), Diagnostics.Type_expected); if (!scanner.hasPrecedingLineBreak() && token() === SyntaxKind.LessThanToken) { node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } @@ -2119,6 +2083,69 @@ namespace ts { return finishNode(node); } + function parseJSDocAllType(): JSDocAllType { + const result = createNode(SyntaxKind.JSDocAllType); + nextToken(); + return finishNode(result); + } + + function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { + const pos = scanner.getStartPos(); + // skip the ? + nextToken(); + + // Need to lookahead to decide if this is a nullable or unknown type. + + // Here are cases where we'll pick the unknown type: + // + // Foo(?, + // { a: ? } + // Foo(?) + // Foo + // Foo(?= + // (?| + if (token() === SyntaxKind.CommaToken || + token() === SyntaxKind.CloseBraceToken || + token() === SyntaxKind.CloseParenToken || + token() === SyntaxKind.GreaterThanToken || + token() === SyntaxKind.EqualsToken || + token() === SyntaxKind.BarToken) { + + const result = createNode(SyntaxKind.JSDocUnknownType, pos); + return finishNode(result); + } + else { + const result = createNode(SyntaxKind.JSDocNullableType, pos); + result.type = parseType(); + return finishNode(result); + } + } + + function parseJSDocFunctionType(): JSDocFunctionType | TypeReferenceNode { + if (lookAhead(nextTokenIsOpenParen)) { + const result = createNode(SyntaxKind.JSDocFunctionType); + nextToken(); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type | SignatureFlags.JSDoc, result); + return finishNode(result); + } + const node = createNode(SyntaxKind.TypeReference); + node.typeName = parseIdentifierName(); + return finishNode(node); + } + + function parseJSDocParameter(): ParameterDeclaration { + const parameter = createNode(SyntaxKind.Parameter); + parameter.type = parseType(); + return finishNode(parameter); + } + + function parseJSDocNodeWithType(kind: SyntaxKind): TypeNode { + const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType | JSDocThisType | JSDocConstructorType; + nextToken(); + result.type = parseType(); + return finishNode(result); + } + function parseTypeQuery(): TypeQueryNode { const node = createNode(SyntaxKind.TypeQuery); parseExpected(SyntaxKind.TypeOfKeyword); @@ -2171,7 +2198,7 @@ namespace ts { } function isStartOfParameter(): boolean { - return token() === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token()) || token() === SyntaxKind.AtToken || token() === SyntaxKind.ThisKeyword; + return token() === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifierKind(token()) || token() === SyntaxKind.AtToken || token() === SyntaxKind.ThisKeyword || token() === SyntaxKind.NewKeyword; } function parseParameter(): ParameterDeclaration { @@ -2229,7 +2256,9 @@ namespace ts { returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, flags: SignatureFlags, signature: SignatureDeclaration): void { - signature.typeParameters = parseTypeParameters(); + if (!(flags & SignatureFlags.JSDoc)) { + signature.typeParameters = parseTypeParameters(); + } signature.parameters = parseParameterList(flags); const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; @@ -2273,7 +2302,7 @@ namespace ts { setYieldContext(!!(flags & SignatureFlags.Yield)); setAwaitContext(!!(flags & SignatureFlags.Await)); - const result = parseDelimitedList(ParsingContext.Parameters, parseParameter); + const result = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter); setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); @@ -2538,10 +2567,14 @@ namespace ts { return finishNode(node); } - function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode { + function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode | JSDocConstructorType { const node = createNode(kind); if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); + if (token() === SyntaxKind.ColonToken) { + // JSDoc -- `new:T` as in `function(new:T, string, string)`; an infix constructor-return-type + return parseJSDocNodeWithType(SyntaxKind.JSDocConstructorType) as JSDocConstructorType; + } } fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node); return finishNode(node); @@ -2574,8 +2607,17 @@ namespace ts { case SyntaxKind.NeverKeyword: case SyntaxKind.ObjectKeyword: // If these are followed by a dot, then parse these out as a dotted type reference instead. - const node = tryParse(parseKeywordAndNoDot); - return node || parseTypeReference(); + return tryParse(parseKeywordAndNoDot) || parseTypeReference(); + case SyntaxKind.AsteriskToken: + return parseJSDocAllType(); + case SyntaxKind.QuestionToken: + return parseJSDocUnknownOrNullableType(); + case SyntaxKind.FunctionKeyword: + return parseJSDocFunctionType(); + case SyntaxKind.DotDotDotToken: + return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType); + case SyntaxKind.ExclamationToken: + return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType); case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.TrueKeyword: @@ -2591,6 +2633,9 @@ namespace ts { if (token() === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { return parseThisTypePredicate(thisKeyword); } + else if (token() === SyntaxKind.ColonToken) { + return parseJSDocNodeWithType(SyntaxKind.JSDocThisType); + } else { return thisKeyword; } @@ -2649,6 +2694,26 @@ namespace ts { return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType(); } + function parseJSDocPostfixTypeOrHigher(): TypeNode { + let type = parseArrayTypeOrHigher(); + let postfix: JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; + // only parse postfix = inside jsdoc, because it's ambiguous elsewhere + if (contextFlags & NodeFlags.JSDoc && parseOptional(SyntaxKind.EqualsToken)) { + postfix = createNode(SyntaxKind.JSDocOptionalType, type.pos) as JSDocOptionalType; + } + else if (parseOptional(SyntaxKind.ExclamationToken)) { + postfix = createNode(SyntaxKind.JSDocNonNullableType, type.pos) as JSDocNonNullableType; + } + else if (parseOptional(SyntaxKind.QuestionToken)) { + postfix = createNode(SyntaxKind.JSDocNullableType, type.pos) as JSDocNullableType; + } + if (postfix) { + postfix.type = type; + type = finishNode(postfix); + } + return type; + } + function parseArrayTypeOrHigher(): TypeNode { let type = parseNonArrayType(); while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { @@ -2682,7 +2747,7 @@ namespace ts { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); } - return parseArrayTypeOrHigher(); + return parseJSDocPostfixTypeOrHigher(); } function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode { @@ -6015,10 +6080,6 @@ namespace ts { TupleElementTypes, // Element types in tuple element type list HeritageClauses, // Heritage clauses for a class or interface declaration. ImportOrExportSpecifiers, // Named import clause's import specifier list - JSDocFunctionParameters, - JSDocTypeArguments, - JSDocRecordMembers, - JSDocTupleTypes, Count // Number of parsing contexts } @@ -6029,24 +6090,6 @@ namespace ts { } export namespace JSDocParser { - export function isJSDocType() { - switch (token()) { - case SyntaxKind.AsteriskToken: - case SyntaxKind.QuestionToken: - case SyntaxKind.OpenParenToken: - case SyntaxKind.OpenBracketToken: - case SyntaxKind.ExclamationToken: - case SyntaxKind.OpenBraceToken: - case SyntaxKind.FunctionKeyword: - case SyntaxKind.DotDotDotToken: - case SyntaxKind.NewKeyword: - case SyntaxKind.ThisKeyword: - return true; - } - - return tokenIsIdentifierOrKeyword(token()); - } - export function parseJSDocTypeExpressionForTests(content: string, start: number, length: number) { initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); sourceFile = createSourceFile("file.js", ScriptTarget.Latest, ScriptKind.JS); @@ -6065,308 +6108,13 @@ namespace ts { const result = createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos()); parseExpected(SyntaxKind.OpenBraceToken); - result.type = parseJSDocTopLevelType(); + result.type = doInsideOfContext(NodeFlags.JSDoc, parseType); parseExpected(SyntaxKind.CloseBraceToken); fixupParentReferences(result); return finishNode(result); } - function parseJSDocTopLevelType(): JSDocType { - let type = parseJSDocType(); - if (token() === SyntaxKind.BarToken) { - const unionType = createNode(SyntaxKind.JSDocUnionType, type.pos); - unionType.types = parseJSDocTypeList(type); - type = finishNode(unionType); - } - - if (token() === SyntaxKind.EqualsToken) { - const optionalType = createNode(SyntaxKind.JSDocOptionalType, type.pos); - nextToken(); - optionalType.type = type; - type = finishNode(optionalType); - } - - return type; - } - - function parseJSDocType(): JSDocType { - let type = parseBasicTypeExpression(); - - while (true) { - if (token() === SyntaxKind.OpenBracketToken) { - const arrayType = createNode(SyntaxKind.JSDocArrayType, type.pos); - arrayType.elementType = type; - - nextToken(); - parseExpected(SyntaxKind.CloseBracketToken); - - type = finishNode(arrayType); - } - else if (token() === SyntaxKind.QuestionToken) { - const nullableType = createNode(SyntaxKind.JSDocNullableType, type.pos); - nullableType.type = type; - - nextToken(); - type = finishNode(nullableType); - } - else if (token() === SyntaxKind.ExclamationToken) { - const nonNullableType = createNode(SyntaxKind.JSDocNonNullableType, type.pos); - nonNullableType.type = type; - - nextToken(); - type = finishNode(nonNullableType); - } - else { - break; - } - } - - return type; - } - - function parseBasicTypeExpression(): JSDocType { - switch (token()) { - case SyntaxKind.AsteriskToken: - return parseJSDocAllType(); - case SyntaxKind.QuestionToken: - return parseJSDocUnknownOrNullableType(); - case SyntaxKind.OpenParenToken: - return parseJSDocUnionType(); - case SyntaxKind.OpenBracketToken: - return parseJSDocTupleType(); - case SyntaxKind.ExclamationToken: - return parseJSDocNonNullableType(); - case SyntaxKind.OpenBraceToken: - return parseJSDocRecordType(); - case SyntaxKind.FunctionKeyword: - if (lookAhead(nextTokenIsOpenParen)) { - return parseJSDocFunctionType(); - } - break; - case SyntaxKind.DotDotDotToken: - return parseJSDocVariadicType(); - case SyntaxKind.NewKeyword: - return parseJSDocConstructorType(); - case SyntaxKind.ThisKeyword: - return parseJSDocThisType(); - case SyntaxKind.AnyKeyword: - case SyntaxKind.StringKeyword: - case SyntaxKind.NumberKeyword: - case SyntaxKind.BooleanKeyword: - case SyntaxKind.SymbolKeyword: - case SyntaxKind.VoidKeyword: - case SyntaxKind.NullKeyword: - case SyntaxKind.UndefinedKeyword: - case SyntaxKind.NeverKeyword: - return parseTokenNode(); - case SyntaxKind.StringLiteral: - case SyntaxKind.NumericLiteral: - case SyntaxKind.TrueKeyword: - case SyntaxKind.FalseKeyword: - return parseJSDocLiteralType(); - } - - return parseJSDocTypeReference(); - } - - function parseJSDocThisType(): JSDocThisType { - const result = createNode(SyntaxKind.JSDocThisType); - nextToken(); - parseExpected(SyntaxKind.ColonToken); - result.type = parseJSDocType(); - return finishNode(result); - } - - function parseJSDocConstructorType(): JSDocConstructorType { - const result = createNode(SyntaxKind.JSDocConstructorType); - nextToken(); - parseExpected(SyntaxKind.ColonToken); - result.type = parseJSDocType(); - return finishNode(result); - } - - function parseJSDocVariadicType(): JSDocVariadicType { - const result = createNode(SyntaxKind.JSDocVariadicType); - nextToken(); - result.type = parseJSDocType(); - return finishNode(result); - } - - function parseJSDocFunctionType(): JSDocFunctionType { - const result = createNode(SyntaxKind.JSDocFunctionType); - nextToken(); - - parseExpected(SyntaxKind.OpenParenToken); - result.parameters = parseDelimitedList(ParsingContext.JSDocFunctionParameters, parseJSDocParameter); - checkForTrailingComma(result.parameters); - parseExpected(SyntaxKind.CloseParenToken); - - if (token() === SyntaxKind.ColonToken) { - nextToken(); - result.type = parseJSDocType(); - } - - return finishNode(result); - } - - function parseJSDocParameter(): ParameterDeclaration { - const parameter = createNode(SyntaxKind.Parameter); - parameter.type = parseJSDocType(); - if (parseOptional(SyntaxKind.EqualsToken)) { - // TODO(rbuckton): Can this be changed to SyntaxKind.QuestionToken? - parameter.questionToken = createNode(SyntaxKind.EqualsToken); - } - return finishNode(parameter); - } - - function parseJSDocTypeReference(): JSDocTypeReference { - const result = createNode(SyntaxKind.JSDocTypeReference); - result.name = parseSimplePropertyName(); - - if (token() === SyntaxKind.LessThanToken) { - result.typeArguments = parseTypeArguments(); - } - else { - while (parseOptional(SyntaxKind.DotToken)) { - if (token() === SyntaxKind.LessThanToken) { - result.typeArguments = parseTypeArguments(); - break; - } - else { - result.name = parseQualifiedName(result.name); - } - } - } - - - return finishNode(result); - } - - function parseTypeArguments() { - // Move past the < - nextToken(); - const typeArguments = parseDelimitedList(ParsingContext.JSDocTypeArguments, parseJSDocType); - checkForTrailingComma(typeArguments); - checkForEmptyTypeArgumentList(typeArguments); - parseExpected(SyntaxKind.GreaterThanToken); - - return typeArguments; - } - - function checkForEmptyTypeArgumentList(typeArguments: NodeArray) { - if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) { - const start = typeArguments.pos - "<".length; - const end = skipTrivia(sourceText, typeArguments.end) + ">".length; - return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty); - } - } - - function parseQualifiedName(left: EntityName): QualifiedName { - const result = createNode(SyntaxKind.QualifiedName, left.pos); - result.left = left; - result.right = parseIdentifierName(); - - return finishNode(result); - } - - function parseJSDocRecordType(): JSDocRecordType { - const result = createNode(SyntaxKind.JSDocRecordType); - result.literal = parseTypeLiteral(); - return finishNode(result); - } - - function parseJSDocNonNullableType(): JSDocNonNullableType { - const result = createNode(SyntaxKind.JSDocNonNullableType); - nextToken(); - result.type = parseJSDocType(); - return finishNode(result); - } - - function parseJSDocTupleType(): JSDocTupleType { - const result = createNode(SyntaxKind.JSDocTupleType); - nextToken(); - result.types = parseDelimitedList(ParsingContext.JSDocTupleTypes, parseJSDocType); - checkForTrailingComma(result.types); - parseExpected(SyntaxKind.CloseBracketToken); - - return finishNode(result); - } - - function checkForTrailingComma(list: NodeArray) { - if (parseDiagnostics.length === 0 && list.hasTrailingComma) { - const start = list.end - ",".length; - parseErrorAtPosition(start, ",".length, Diagnostics.Trailing_comma_not_allowed); - } - } - - function parseJSDocUnionType(): JSDocUnionType { - const result = createNode(SyntaxKind.JSDocUnionType); - nextToken(); - result.types = parseJSDocTypeList(parseJSDocType()); - - parseExpected(SyntaxKind.CloseParenToken); - - return finishNode(result); - } - - function parseJSDocTypeList(firstType: JSDocType) { - Debug.assert(!!firstType); - - const types = createNodeArray([firstType], firstType.pos); - - while (parseOptional(SyntaxKind.BarToken)) { - types.push(parseJSDocType()); - } - - types.end = scanner.getStartPos(); - return types; - } - - function parseJSDocAllType(): JSDocAllType { - const result = createNode(SyntaxKind.JSDocAllType); - nextToken(); - return finishNode(result); - } - - function parseJSDocLiteralType(): JSDocLiteralType { - const result = createNode(SyntaxKind.JSDocLiteralType); - result.literal = parseLiteralTypeNode(); - return finishNode(result); - } - - function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { - const pos = scanner.getStartPos(); - // skip the ? - nextToken(); - - // Need to lookahead to decide if this is a nullable or unknown type. - - // Here are cases where we'll pick the unknown type: - // - // Foo(?, - // { a: ? } - // Foo(?) - // Foo - // Foo(?= - // (?| - if (token() === SyntaxKind.CommaToken || - token() === SyntaxKind.CloseBraceToken || - token() === SyntaxKind.CloseParenToken || - token() === SyntaxKind.GreaterThanToken || - token() === SyntaxKind.EqualsToken || - token() === SyntaxKind.BarToken) { - - const result = createNode(SyntaxKind.JSDocUnknownType, pos); - return finishNode(result); - } - else { - const result = createNode(SyntaxKind.JSDocNullableType, pos); - result.type = parseJSDocType(); - return finishNode(result); - } - } - export function parseIsolatedJSDocComment(content: string, start: number, length: number) { initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; @@ -6820,14 +6568,8 @@ namespace ts { skipWhitespace(); if (typeExpression) { - if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { - const jsDocTypeReference = typeExpression.type; - if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { - const name = jsDocTypeReference.name; - if (name.text === "Object" || name.text === "object") { - typedefTag.jsDocTypeLiteral = scanChildTags(); - } - } + if (isObjectTypeReference(typeExpression.type)) { + typedefTag.jsDocTypeLiteral = scanChildTags(); } if (!typedefTag.jsDocTypeLiteral) { typedefTag.jsDocTypeLiteral = typeExpression.type; @@ -6839,6 +6581,18 @@ namespace ts { return finishNode(typedefTag); + function isObjectTypeReference(node: TypeNode) { + if (node.kind === SyntaxKind.ObjectKeyword) { + return true; + } + if (node.kind === SyntaxKind.TypeReference) { + const jsDocTypeReference = node; + if (jsDocTypeReference.typeName.kind === SyntaxKind.Identifier) { + return (jsDocTypeReference.typeName as Identifier).text === "Object"; + } + } + } + function scanChildTags(): JSDocTypeLiteral { const jsDocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos()); let resumePos = scanner.getStartPos(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c9309a7d844..b2fb24fa0e4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -347,14 +347,8 @@ namespace ts { JSDocAllType, // The ? type JSDocUnknownType, - JSDocArrayType, - JSDocUnionType, - JSDocTupleType, JSDocNullableType, JSDocNonNullableType, - JSDocRecordType, - JSDocRecordMember, - JSDocTypeReference, JSDocOptionalType, JSDocFunctionType, JSDocVariadicType, @@ -371,7 +365,6 @@ namespace ts { JSDocTypedefTag, JSDocPropertyTag, JSDocTypeLiteral, - JSDocLiteralType, // Synthesized list SyntaxList, @@ -413,9 +406,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocLiteralType, + LastJSDocNode = JSDocTypeLiteral, FirstJSDocTagNode = JSDocTag, - LastJSDocTagNode = JSDocLiteralType + LastJSDocTagNode = JSDocTypeLiteral } export const enum NodeFlags { @@ -450,6 +443,7 @@ namespace ts { // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. /* @internal */ PossiblyContainsDynamicImport = 1 << 19, + JSDoc = 1 << 20, // If node was parsed inside jsdoc BlockScoped = Let | Const, @@ -584,6 +578,7 @@ namespace ts { /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics. + /*@internal*/ jsdocDot?: boolean; // Identifier occurs in JSDoc-style generic: Id. } // Transient identifier node (marked by id === -1) @@ -603,6 +598,7 @@ namespace ts { kind: SyntaxKind.QualifiedName; left: EntityName; right: Identifier; + /*@internal*/ jsdocDot?: boolean; // Identifier occurs in JSDoc-style generic: Id1.Id2. } export type EntityName = Identifier | QualifiedName; @@ -694,7 +690,7 @@ namespace ts { initializer?: Expression; // Optional initializer } - export interface TSPropertySignature extends TypeElement { + export interface PropertySignature extends TypeElement { kind: SyntaxKind.PropertySignature; name: PropertyName; // Declared property name questionToken?: QuestionToken; // Present on optional property @@ -702,16 +698,6 @@ namespace ts { initializer?: Expression; // Optional initializer } - export interface JSDocPropertySignature extends TypeElement { - kind: SyntaxKind.JSDocRecordMember; - name: PropertyName; // Declared property name - questionToken?: QuestionToken; // Present on optional property - type?: TypeNode; // Optional type annotation - initializer?: Expression; // Optional initializer - } - - export type PropertySignature = TSPropertySignature | JSDocPropertySignature; - export interface PropertyDeclaration extends ClassElement { kind: SyntaxKind.PropertyDeclaration; questionToken?: QuestionToken; // Present for use with reporting a grammar error @@ -921,7 +907,7 @@ namespace ts { kind: SyntaxKind.ConstructorType; } - export type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference; + export type TypeReferenceType = TypeReferenceNode | ExpressionWithTypeArguments; export interface TypeReferenceNode extends TypeNode { kind: SyntaxKind.TypeReference; @@ -2042,7 +2028,7 @@ namespace ts { // represents a top level: { type } expression in a JSDoc comment. export interface JSDocTypeExpression extends Node { kind: SyntaxKind.JSDocTypeExpression; - type: JSDocType; + type: TypeNode; } export interface JSDocType extends TypeNode { @@ -2057,81 +2043,42 @@ namespace ts { kind: SyntaxKind.JSDocUnknownType; } - export interface JSDocArrayType extends JSDocType { - kind: SyntaxKind.JSDocArrayType; - elementType: JSDocType; - } - - export interface JSDocUnionType extends JSDocType { - kind: SyntaxKind.JSDocUnionType; - types: NodeArray; - } - - export interface JSDocTupleType extends JSDocType { - kind: SyntaxKind.JSDocTupleType; - types: NodeArray; - } - export interface JSDocNonNullableType extends JSDocType { kind: SyntaxKind.JSDocNonNullableType; - type: JSDocType; + type: TypeNode; } export interface JSDocNullableType extends JSDocType { kind: SyntaxKind.JSDocNullableType; - type: JSDocType; - } - - export interface JSDocRecordType extends JSDocType { - kind: SyntaxKind.JSDocRecordType; - literal: TypeLiteralNode; - } - - export interface JSDocTypeReference extends JSDocType { - kind: SyntaxKind.JSDocTypeReference; - name: EntityName; - typeArguments: NodeArray; + type: TypeNode; } export interface JSDocOptionalType extends JSDocType { kind: SyntaxKind.JSDocOptionalType; - type: JSDocType; + type: TypeNode; } export interface JSDocFunctionType extends JSDocType, SignatureDeclaration { kind: SyntaxKind.JSDocFunctionType; - parameters: NodeArray; - type: JSDocType; } export interface JSDocVariadicType extends JSDocType { kind: SyntaxKind.JSDocVariadicType; - type: JSDocType; + type: TypeNode; } export interface JSDocConstructorType extends JSDocType { kind: SyntaxKind.JSDocConstructorType; - type: JSDocType; + type: TypeNode; } export interface JSDocThisType extends JSDocType { kind: SyntaxKind.JSDocThisType; - type: JSDocType; - } - - export interface JSDocLiteralType extends JSDocType { - kind: SyntaxKind.JSDocLiteralType; - literal: LiteralTypeNode; + type: TypeNode; } export type JSDocTypeReferencingNode = JSDocThisType | JSDocConstructorType | JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; - export interface JSDocRecordMember extends JSDocPropertySignature { - kind: SyntaxKind.JSDocRecordMember; - name: Identifier | StringLiteral | NumericLiteral; - type?: JSDocType; - } - export interface JSDoc extends Node { kind: SyntaxKind.JSDocComment; tags: NodeArray | undefined; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c8751f497a6..f7356f425fe 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1082,7 +1082,6 @@ namespace ts { export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { switch (node.kind) { case SyntaxKind.TypeReference: - case SyntaxKind.JSDocTypeReference: return (node).typeName; case SyntaxKind.ExpressionWithTypeArguments: @@ -1582,7 +1581,7 @@ namespace ts { return find(typeParameters, p => p.name.text === name); } - export function getJSDocType(node: Node): JSDocType { + export function getJSDocType(node: Node): TypeNode { let tag: JSDocTypeTag | JSDocParameterTag = getFirstJSDocTag(node, SyntaxKind.JSDocTypeTag) as JSDocTypeTag; if (!tag && node.kind === SyntaxKind.Parameter) { const paramTags = getJSDocParameterTags(node as ParameterDeclaration); @@ -1606,7 +1605,7 @@ namespace ts { return getFirstJSDocTag(node, SyntaxKind.JSDocReturnTag) as JSDocReturnTag; } - export function getJSDocReturnType(node: Node): JSDocType { + export function getJSDocReturnType(node: Node): TypeNode { const returnTag = getJSDocReturnTag(node); return returnTag && returnTag.typeExpression && returnTag.typeExpression.type; } @@ -3298,6 +3297,10 @@ namespace ts { return false; } + export function isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode { + return node.kind === SyntaxKind.TypeReference && !!findAncestor(node, n => n.kind === SyntaxKind.JSDocTypeExpression); + } + /** * Formats an enum value as a string for debugging and debug assertions. */ @@ -4664,18 +4667,6 @@ namespace ts { return node.kind === SyntaxKind.JSDocUnknownType; } - export function isJSDocArrayType(node: Node): node is JSDocArrayType { - return node.kind === SyntaxKind.JSDocArrayType; - } - - export function isJSDocUnionType(node: Node): node is JSDocUnionType { - return node.kind === SyntaxKind.JSDocUnionType; - } - - export function isJSDocTupleType(node: Node): node is JSDocTupleType { - return node.kind === SyntaxKind.JSDocTupleType; - } - export function isJSDocNullableType(node: Node): node is JSDocNullableType { return node.kind === SyntaxKind.JSDocNullableType; } @@ -4684,18 +4675,6 @@ namespace ts { return node.kind === SyntaxKind.JSDocNonNullableType; } - export function isJSDocRecordType(node: Node): node is JSDocRecordType { - return node.kind === SyntaxKind.JSDocRecordType; - } - - export function isJSDocRecordMember(node: Node): node is JSDocRecordMember { - return node.kind === SyntaxKind.JSDocRecordMember; - } - - export function isJSDocTypeReference(node: Node): node is JSDocTypeReference { - return node.kind === SyntaxKind.JSDocTypeReference; - } - export function isJSDocOptionalType(node: Node): node is JSDocOptionalType { return node.kind === SyntaxKind.JSDocOptionalType; } @@ -4751,10 +4730,6 @@ namespace ts { export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral { return node.kind === SyntaxKind.JSDocTypeLiteral; } - - export function isJSDocLiteralType(node: Node): node is JSDocLiteralType { - return node.kind === SyntaxKind.JSDocLiteralType; - } } // Node tests diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 309e49bd6b8..032c4e848b5 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -62,6 +62,12 @@ namespace ts { parsesCorrectly("tupleType1", "{[number]}"); parsesCorrectly("tupleType2", "{[number,string]}"); parsesCorrectly("tupleType3", "{[number,string,boolean]}"); + parsesCorrectly("tupleTypeWithTrailingComma", "{[number,]}"); + parsesCorrectly("typeOfType", "{typeof M}"); + parsesCorrectly("tsConstructoType", "{new () => string}"); + parsesCorrectly("tsFunctionType", "{() => string}"); + parsesCorrectly("typeArgumentsNotFollowingDot", "{a<>}"); + parsesCorrectly("functionTypeWithTrailingComma", "{function(a,)}"); }); describe("parsesIncorrectly", () => { @@ -69,21 +75,13 @@ namespace ts { parsesIncorrectly("unionTypeWithTrailingBar", "{(a|)}"); parsesIncorrectly("unionTypeWithoutTypes", "{()}"); parsesIncorrectly("nullableTypeWithoutType", "{!}"); - parsesIncorrectly("functionTypeWithTrailingComma", "{function(a,)}"); parsesIncorrectly("thisWithoutType", "{this:}"); parsesIncorrectly("newWithoutType", "{new:}"); parsesIncorrectly("variadicWithoutType", "{...}"); parsesIncorrectly("optionalWithoutType", "{=}"); parsesIncorrectly("allWithType", "{*foo}"); - parsesIncorrectly("typeArgumentsNotFollowingDot", "{a<>}"); - parsesIncorrectly("emptyTypeArguments", "{a.<>}"); - parsesIncorrectly("typeArgumentsWithTrailingComma", "{a.}"); - parsesIncorrectly("tsFunctionType", "{() => string}"); - parsesIncorrectly("tsConstructoType", "{new () => string}"); - parsesIncorrectly("typeOfType", "{typeof M}"); parsesIncorrectly("namedParameter", "{function(a: number)}"); parsesIncorrectly("tupleTypeWithComma", "{[,]}"); - parsesIncorrectly("tupleTypeWithTrailingComma", "{[number,]}"); parsesIncorrectly("tupleTypeWithLeadingComma", "{[,number]}"); }); }); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 7fcfffc3ee2..0e92884d502 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -176,7 +176,6 @@ namespace ts { switch (node.parent.kind) { case SyntaxKind.TypeReference: - case SyntaxKind.JSDocTypeReference: return true; case SyntaxKind.ExpressionWithTypeArguments: return !isExpressionWithTypeArgumentsInClassExtendsClause(node.parent);