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 #16550
This commit is contained in:
Nathan Shively-Sanders 2017-07-13 11:27:50 -07:00
parent fd2dd2edc0
commit bc69c7e6d1
8 changed files with 254 additions and 542 deletions

View file

@ -280,7 +280,14 @@ namespace ts {
Debug.assert(node.parent.kind === SyntaxKind.JSDocFunctionType);
const functionType = <JSDocFunctionType>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(<Declaration>node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
case SyntaxKind.FunctionType:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.ConstructorType:
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
case SyntaxKind.TypeLiteral:
@ -2157,18 +2163,13 @@ namespace ts {
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((<Block | ModuleBlock>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(<SignatureDeclaration>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(<Declaration>node, SymbolFlags.TypeLiteral, InternalSymbolName.Type);
}

View file

@ -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 (<TypeReferenceNode>node).typeName;
case SyntaxKind.JSDocTypeReference:
return (<JSDocTypeReference>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(<LiteralTypeNode>node);
case SyntaxKind.JSDocLiteralType:
return getTypeFromLiteralTypeNode((<JSDocLiteralType>node).literal);
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return getTypeFromTypeReference(<TypeReferenceNode>node);
case SyntaxKind.TypePredicate:
return booleanType;
@ -7870,12 +7862,10 @@ namespace ts {
case SyntaxKind.TypeQuery:
return getTypeFromTypeQueryNode(<TypeQueryNode>node);
case SyntaxKind.ArrayType:
case SyntaxKind.JSDocArrayType:
return getTypeFromArrayTypeNode(<ArrayTypeNode>node);
case SyntaxKind.TupleType:
return getTypeFromTupleTypeNode(<TupleTypeNode>node);
case SyntaxKind.UnionType:
case SyntaxKind.JSDocUnionType:
return getTypeFromUnionTypeNode(<UnionTypeNode>node);
case SyntaxKind.IntersectionType:
return getTypeFromIntersectionTypeNode(<IntersectionTypeNode>node);
@ -7887,8 +7877,6 @@ namespace ts {
case SyntaxKind.JSDocThisType:
case SyntaxKind.JSDocOptionalType:
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>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(<JSDocTupleType>node);
case SyntaxKind.JSDocVariadicType:
return getTypeFromJSDocVariadicType(<JSDocVariadicType>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((<ParenthesizedTypeNode | TypeOperatorNode>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(<IndexedAccessTypeNode>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>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>entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/ true);
}
else if (entityName.parent.kind === SyntaxKind.JsxAttribute) {

View file

@ -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
}
}

View file

@ -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, (<JSDocTypeExpression>node).type);
case SyntaxKind.JSDocUnionType:
return visitNodes(cbNode, cbNodes, (<JSDocUnionType>node).types);
case SyntaxKind.JSDocTupleType:
return visitNodes(cbNode, cbNodes, (<JSDocTupleType>node).types);
case SyntaxKind.JSDocArrayType:
return visitNode(cbNode, (<JSDocArrayType>node).elementType);
case SyntaxKind.JSDocNonNullableType:
return visitNode(cbNode, (<JSDocNonNullableType>node).type);
case SyntaxKind.JSDocNullableType:
return visitNode(cbNode, (<JSDocNullableType>node).type);
case SyntaxKind.JSDocRecordType:
return visitNode(cbNode, (<JSDocRecordType>node).literal);
case SyntaxKind.JSDocTypeReference:
return visitNode(cbNode, (<JSDocTypeReference>node).name) ||
visitNodes(cbNode, cbNodes, (<JSDocTypeReference>node).typeArguments);
case SyntaxKind.JSDocOptionalType:
return visitNode(cbNode, (<JSDocOptionalType>node).type);
case SyntaxKind.JSDocFunctionType:
@ -418,9 +408,6 @@ namespace ts {
return visitNode(cbNode, (<JSDocConstructorType>node).type);
case SyntaxKind.JSDocThisType:
return visitNode(cbNode, (<JSDocThisType>node).type);
case SyntaxKind.JSDocRecordMember:
return visitNode(cbNode, (<JSDocRecordMember>node).name) ||
visitNode(cbNode, (<JSDocRecordMember>node).type);
case SyntaxKind.JSDocComment:
return visitNodes(cbNode, cbNodes, (<JSDoc>node).tags);
case SyntaxKind.JSDocParameterTag:
@ -447,8 +434,6 @@ namespace ts {
visitNode(cbNode, (<JSDocPropertyTag>node).name);
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
case SyntaxKind.JSDocLiteralType:
return visitNode(cbNode, (<JSDocLiteralType>node).literal);
}
}
@ -1239,14 +1224,6 @@ namespace ts {
return parsePropertyNameWorker(/*allowComputedPropertyNames*/ true);
}
function parseSimplePropertyName(): Identifier | LiteralExpression {
return <Identifier | LiteralExpression>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 = <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 = <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 = <TypeReferenceNode>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 = <JSDocAllType>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 = <JSDocUnknownType>createNode(SyntaxKind.JSDocUnknownType, pos);
return finishNode(result);
}
else {
const result = <JSDocNullableType>createNode(SyntaxKind.JSDocNullableType, pos);
result.type = parseType();
return finishNode(result);
}
}
function parseJSDocFunctionType(): JSDocFunctionType | TypeReferenceNode {
if (lookAhead(nextTokenIsOpenParen)) {
const result = <JSDocFunctionType>createNode(SyntaxKind.JSDocFunctionType);
nextToken();
fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type | SignatureFlags.JSDoc, result);
return finishNode(result);
}
const node = <TypeReferenceNode>createNode(SyntaxKind.TypeReference);
node.typeName = parseIdentifierName();
return finishNode(node);
}
function parseJSDocParameter(): ParameterDeclaration {
const parameter = <ParameterDeclaration>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 = <TypeQueryNode>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 = <FunctionOrConstructorTypeNode>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 = <JSDocTypeExpression>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 = <JSDocUnionType>createNode(SyntaxKind.JSDocUnionType, type.pos);
unionType.types = parseJSDocTypeList(type);
type = finishNode(unionType);
}
if (token() === SyntaxKind.EqualsToken) {
const optionalType = <JSDocOptionalType>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 = <JSDocArrayType>createNode(SyntaxKind.JSDocArrayType, type.pos);
arrayType.elementType = type;
nextToken();
parseExpected(SyntaxKind.CloseBracketToken);
type = finishNode(arrayType);
}
else if (token() === SyntaxKind.QuestionToken) {
const nullableType = <JSDocNullableType>createNode(SyntaxKind.JSDocNullableType, type.pos);
nullableType.type = type;
nextToken();
type = finishNode(nullableType);
}
else if (token() === SyntaxKind.ExclamationToken) {
const nonNullableType = <JSDocNonNullableType>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<JSDocType>();
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
return parseJSDocLiteralType();
}
return parseJSDocTypeReference();
}
function parseJSDocThisType(): JSDocThisType {
const result = <JSDocThisType>createNode(SyntaxKind.JSDocThisType);
nextToken();
parseExpected(SyntaxKind.ColonToken);
result.type = parseJSDocType();
return finishNode(result);
}
function parseJSDocConstructorType(): JSDocConstructorType {
const result = <JSDocConstructorType>createNode(SyntaxKind.JSDocConstructorType);
nextToken();
parseExpected(SyntaxKind.ColonToken);
result.type = parseJSDocType();
return finishNode(result);
}
function parseJSDocVariadicType(): JSDocVariadicType {
const result = <JSDocVariadicType>createNode(SyntaxKind.JSDocVariadicType);
nextToken();
result.type = parseJSDocType();
return finishNode(result);
}
function parseJSDocFunctionType(): JSDocFunctionType {
const result = <JSDocFunctionType>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 = <ParameterDeclaration>createNode(SyntaxKind.Parameter);
parameter.type = parseJSDocType();
if (parseOptional(SyntaxKind.EqualsToken)) {
// TODO(rbuckton): Can this be changed to SyntaxKind.QuestionToken?
parameter.questionToken = <QuestionToken>createNode(SyntaxKind.EqualsToken);
}
return finishNode(parameter);
}
function parseJSDocTypeReference(): JSDocTypeReference {
const result = <JSDocTypeReference>createNode(SyntaxKind.JSDocTypeReference);
result.name = <Identifier>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<Node>) {
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 = <QualifiedName>createNode(SyntaxKind.QualifiedName, left.pos);
result.left = left;
result.right = parseIdentifierName();
return finishNode(result);
}
function parseJSDocRecordType(): JSDocRecordType {
const result = <JSDocRecordType>createNode(SyntaxKind.JSDocRecordType);
result.literal = parseTypeLiteral();
return finishNode(result);
}
function parseJSDocNonNullableType(): JSDocNonNullableType {
const result = <JSDocNonNullableType>createNode(SyntaxKind.JSDocNonNullableType);
nextToken();
result.type = parseJSDocType();
return finishNode(result);
}
function parseJSDocTupleType(): JSDocTupleType {
const result = <JSDocTupleType>createNode(SyntaxKind.JSDocTupleType);
nextToken();
result.types = parseDelimitedList(ParsingContext.JSDocTupleTypes, parseJSDocType);
checkForTrailingComma(result.types);
parseExpected(SyntaxKind.CloseBracketToken);
return finishNode(result);
}
function checkForTrailingComma(list: NodeArray<Node>) {
if (parseDiagnostics.length === 0 && list.hasTrailingComma) {
const start = list.end - ",".length;
parseErrorAtPosition(start, ",".length, Diagnostics.Trailing_comma_not_allowed);
}
}
function parseJSDocUnionType(): JSDocUnionType {
const result = <JSDocUnionType>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 = <JSDocAllType>createNode(SyntaxKind.JSDocAllType);
nextToken();
return finishNode(result);
}
function parseJSDocLiteralType(): JSDocLiteralType {
const result = <JSDocLiteralType>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 = <JSDocUnknownType>createNode(SyntaxKind.JSDocUnknownType, pos);
return finishNode(result);
}
else {
const result = <JSDocNullableType>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 = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content };
@ -6820,14 +6568,8 @@ namespace ts {
skipWhitespace();
if (typeExpression) {
if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) {
const jsDocTypeReference = <JSDocTypeReference>typeExpression.type;
if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) {
const name = <Identifier>jsDocTypeReference.name;
if (name.text === "Object" || name.text === "object") {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
}
if (isObjectTypeReference(typeExpression.type)) {
typedefTag.jsDocTypeLiteral = scanChildTags();
}
if (!typedefTag.jsDocTypeLiteral) {
typedefTag.jsDocTypeLiteral = <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 = <TypeReferenceNode>node;
if (jsDocTypeReference.typeName.kind === SyntaxKind.Identifier) {
return (jsDocTypeReference.typeName as Identifier).text === "Object";
}
}
}
function scanChildTags(): JSDocTypeLiteral {
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
let resumePos = scanner.getStartPos();

View file

@ -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<TypeNode>; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics.
/*@internal*/ jsdocDot?: boolean; // Identifier occurs in JSDoc-style generic: Id.<T>
}
// 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.<T>
}
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<JSDocType>;
}
export interface JSDocTupleType extends JSDocType {
kind: SyntaxKind.JSDocTupleType;
types: NodeArray<JSDocType>;
}
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<JSDocType>;
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<ParameterDeclaration>;
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<JSDocTag> | undefined;

View file

@ -1082,7 +1082,6 @@ namespace ts {
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression {
switch (node.kind) {
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return (<TypeReferenceNode>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

View file

@ -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.<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]}");
});
});

View file

@ -176,7 +176,6 @@ namespace ts {
switch (node.parent.kind) {
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return true;
case SyntaxKind.ExpressionWithTypeArguments:
return !isExpressionWithTypeArgumentsInClassExtendsClause(<ExpressionWithTypeArguments>node.parent);