From e7dff1b4bcfcc82ba72e31c1538d1e14148dfe1f Mon Sep 17 00:00:00 2001 From: Tycho Grouwstra Date: Tue, 22 Aug 2017 02:02:21 +0800 Subject: [PATCH] type-level assertion operator (!)... parsing still fails checker errors with JSDocUnknownType, but didn't parse that? --- src/compiler/checker.ts | 14 +++++++++ src/compiler/emitter.ts | 2 ++ src/compiler/factory.ts | 12 +++++++ src/compiler/parser.ts | 31 ++++++++++++++++++- src/compiler/types.ts | 6 ++++ src/compiler/visitor.ts | 4 +++ .../reference/nonNullType.errors.txt | 11 +++++++ tests/baselines/reference/nonNullType.js | 10 ++++++ tests/cases/compiler/nonNullType.ts | 5 +++ 9 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/nonNullType.errors.txt create mode 100644 tests/baselines/reference/nonNullType.js create mode 100644 tests/cases/compiler/nonNullType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6febaf0052d96..9de307f1979a9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1,5 +1,7 @@ /// /// +/// +declare var console: Console; /* @internal */ namespace ts { @@ -7540,6 +7542,14 @@ namespace ts { return links.resolvedType; } + function getTypeFromPostfixedTypeOperatorNode(node: NonNullTypeNode) { // TypeOperatorNode + const links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = getNonNullableType(getTypeFromTypeNode(node.type)); + } + return links.resolvedType; + } + function createIndexedAccessType(objectType: Type, indexType: Type) { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; @@ -7997,6 +8007,8 @@ namespace ts { return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); case SyntaxKind.TypeOperator: return getTypeFromTypeOperatorNode(node); + case SyntaxKind.NonNullTypeNode: + return getTypeFromPostfixedTypeOperatorNode(node); case SyntaxKind.IndexedAccessType: return getTypeFromIndexedAccessTypeNode(node); case SyntaxKind.MappedType: @@ -18701,6 +18713,7 @@ namespace ts { } function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) { + if (allowSyntheticDefaultImports) console.trace("checkTypeReferenceNode"); checkGrammarTypeArguments(node, node.typeArguments); if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) { grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); @@ -22315,6 +22328,7 @@ namespace ts { case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: if (!isInJavaScriptFile(node) && !isInJSDoc(node)) { + if (allowSyntheticDefaultImports) console.trace("JSDocUnknownType"); grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments); } return; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5444c618353bd..385e2096c73de 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -797,6 +797,8 @@ namespace ts { return emitAsExpression(node); case SyntaxKind.NonNullExpression: return emitNonNullExpression(node); + case SyntaxKind.NonNullTypeNode: + return emitWithSuffix((node).type, "!"); case SyntaxKind.MetaProperty: return emitMetaProperty(node); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index daec1bce1e8b5..ecebbea8f8529 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1261,6 +1261,18 @@ namespace ts { : node; } + export function createNonNullTypeNode(type: TypeNode) { + const node = createSynthesizedNode(SyntaxKind.NonNullTypeNode); + node.type = parenthesizeElementTypeMember(type); + return node; + } + + export function updateNonNullTypeNode(node: NonNullTypeNode, type: TypeNode) { + return node.type !== type + ? updateNode(createNonNullTypeNode(type), node) + : node; + } + export function createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier) { const node = createSynthesizedNode(SyntaxKind.MetaProperty); node.keywordToken = keywordToken; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a2fe752ad18e9..e33fe9dd0db0c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -207,6 +207,8 @@ namespace ts { visitNode(cbNode, (node).type); case SyntaxKind.NonNullExpression: return visitNode(cbNode, (node).expression); + case SyntaxKind.NonNullTypeNode: + return visitNode(cbNode, (node).type); case SyntaxKind.MetaProperty: return visitNode(cbNode, (node).name); case SyntaxKind.ConditionalExpression: @@ -1959,6 +1961,7 @@ namespace ts { while (parseOptional(SyntaxKind.DotToken)) { if (token() === SyntaxKind.LessThanToken) { // the entity is part of a JSDoc-style generic, so record the trailing dot for later error reporting + console.trace("parseEntityName:jsdocDotPos"); entity.jsdocDotPos = dotPos; break; } @@ -2122,6 +2125,7 @@ namespace ts { } function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { + console.trace("parseJSDocUnknownOrNullableType"); const pos = scanner.getStartPos(); // skip the ? nextToken(); @@ -2657,6 +2661,7 @@ namespace ts { case SyntaxKind.AsteriskToken: return parseJSDocAllType(); case SyntaxKind.QuestionToken: + console.trace("parseNonArrayType:QuestionToken"); return parseJSDocUnknownOrNullableType(); case SyntaxKind.FunctionKeyword: return parseJSDocFunctionType(); @@ -2794,7 +2799,31 @@ namespace ts { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); } - return parseArrayTypeOrHigher(); + const type = parseArrayTypeOrHigher(); + return parsePostfixTypeOperatorOrHigher(type); + } + + function parsePostfixTypeOperator(type: TypeNode) { // , operator: SyntaxKind.NonNullTypeNode + // const node = createNode(SyntaxKind.TypeOperator); + const node = createNode(SyntaxKind.NonNullTypeNode); + // parseExpected(operator); + parseExpected(SyntaxKind.ExclamationToken); + // node.operator = operator; + node.type = type; + return finishNode(node); + } + + function parsePostfixTypeOperatorOrHigher(type: TypeNode): TypeNode { + let postfixed: TypeNode; + switch (token()) { + case SyntaxKind.NonNullTypeNode: + return parsePostfixTypeOperator(type); // , SyntaxKind.NonNullTypeNode + } + if (postfixed) { + return parsePostfixTypeOperatorOrHigher(postfixed); + } else { + return type; + } } function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 608bc779042df..38212a58444d0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -272,6 +272,7 @@ namespace ts { ExpressionWithTypeArguments, AsExpression, NonNullExpression, + NonNullTypeNode, MetaProperty, // Misc @@ -1545,6 +1546,11 @@ namespace ts { expression: Expression; } + export interface NonNullTypeNode extends TypeNode { + kind: SyntaxKind.NonNullTypeNode; + type: TypeNode; + } + // NOTE: MetaProperty is really a MemberExpression, but we consider it a PrimaryExpression // for the same reasons we treat NewExpression as a PrimaryExpression. export interface MetaProperty extends PrimaryExpression { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 1ce42199372d8..a563eacdc74b0 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -562,6 +562,10 @@ namespace ts { return updateNonNullExpression(node, visitNode((node).expression, visitor, isExpression)); + case SyntaxKind.NonNullTypeNode: + return updateNonNullTypeNode(node, + visitNode((node).type, visitor, isTypeNode)); + case SyntaxKind.MetaProperty: return updateMetaProperty(node, visitNode((node).name, visitor, isIdentifier)); diff --git a/tests/baselines/reference/nonNullType.errors.txt b/tests/baselines/reference/nonNullType.errors.txt new file mode 100644 index 0000000000000..d682dc2173bdd --- /dev/null +++ b/tests/baselines/reference/nonNullType.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/nonNullType.ts(2,10): error TS8020: JSDoc types can only be used inside documentation comments. + + +==== tests/cases/compiler/nonNullType.ts (1 errors) ==== + type a = string | undefined | null | never; + type b = a!; + ~~ +!!! error TS8020: JSDoc types can only be used inside documentation comments. + // type Assert = T!; + // type c = Assert; + \ No newline at end of file diff --git a/tests/baselines/reference/nonNullType.js b/tests/baselines/reference/nonNullType.js new file mode 100644 index 0000000000000..7cef073af8c6b --- /dev/null +++ b/tests/baselines/reference/nonNullType.js @@ -0,0 +1,10 @@ +//// [nonNullType.ts] +type a = string | undefined | null | never; +type b = a!; +// type Assert = T!; +// type c = Assert; + + +//// [nonNullType.js] +// type Assert = T!; +// type c = Assert; diff --git a/tests/cases/compiler/nonNullType.ts b/tests/cases/compiler/nonNullType.ts new file mode 100644 index 0000000000000..d3962a7828818 --- /dev/null +++ b/tests/cases/compiler/nonNullType.ts @@ -0,0 +1,5 @@ +// @allowSyntheticDefaultImports: true +type a = string | undefined | null | never; +type b = a!; +// type Assert = T!; +// type c = Assert;