diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6febaf0052d96..c653bbeb230da 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7540,6 +7540,10 @@ namespace ts { return links.resolvedType; } + function getTypeFromPostfixedTypeOperatorNode(node: NonNullTypeNode) { // TypeOperatorNode + return getNonNullableType(getTypeFromTypeNode(node.type)); + } + function createIndexedAccessType(objectType: Type, indexType: Type) { const type = createType(TypeFlags.IndexedAccess); type.objectType = objectType; @@ -7985,7 +7989,6 @@ namespace ts { case SyntaxKind.JSDocNullableType: return getTypeFromJSDocNullableTypeNode(node); case SyntaxKind.ParenthesizedType: - case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocOptionalType: case SyntaxKind.JSDocTypeExpression: return getTypeFromTypeNode((node).type); @@ -7997,6 +8000,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 +18706,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); @@ -22310,7 +22316,6 @@ namespace ts { checkSignatureDeclaration(node as JSDocFunctionType); // falls through case SyntaxKind.JSDocVariadicType: - case SyntaxKind.JSDocNonNullableType: case SyntaxKind.JSDocNullableType: case SyntaxKind.JSDocAllType: case SyntaxKind.JSDocUnknownType: 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..a654e3011ec46 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: @@ -396,8 +398,6 @@ namespace ts { case SyntaxKind.JSDocTypeExpression: return visitNode(cbNode, (node).type); - case SyntaxKind.JSDocNonNullableType: - return visitNode(cbNode, (node).type); case SyntaxKind.JSDocNullableType: return visitNode(cbNode, (node).type); case SyntaxKind.JSDocOptionalType: @@ -2175,8 +2175,8 @@ namespace ts { return finishNode(parameter); } - function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType | SyntaxKind.JSDocNonNullableType): TypeNode { - const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType; + function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType): TypeNode { + const result = createNode(kind) as JSDocVariadicType; nextToken(); result.type = parseType(); return finishNode(result); @@ -2662,8 +2662,6 @@ namespace ts { 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: @@ -2744,7 +2742,7 @@ namespace ts { if (!kind) return type; nextToken(); - const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; + const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNullableType; postfix.type = type; return finishNode(postfix); @@ -2753,8 +2751,6 @@ namespace ts { case SyntaxKind.EqualsToken: // only parse postfix = inside jsdoc, because it's ambiguous elsewhere return contextFlags & NodeFlags.JSDoc ? SyntaxKind.JSDocOptionalType : undefined; - case SyntaxKind.ExclamationToken: - return SyntaxKind.JSDocNonNullableType; case SyntaxKind.QuestionToken: return SyntaxKind.JSDocNullableType; } @@ -2794,7 +2790,36 @@ namespace ts { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); } - return parseArrayTypeOrHigher(); + const type = parseArrayTypeOrHigher(); + return parsePostfixTypeOperatorOrHigher(type); + } + + function parsePostfixTypeOperator(type: TypeNode) { // , parseKind: SyntaxKind, nodeKind: SyntaxKind + const node = createNode(SyntaxKind.NonNullTypeNode); + // const node = createNode(nodeKind); + // parseExpected(operator); + parseExpected(SyntaxKind.ExclamationToken); + // parseExpected(parseKind); + // node.operator = operator; + node.type = type; + const finished = finishNode(node); + finished.pos = type.pos; + // finished.end = type.end + 1; + return finished; + } + + function parsePostfixTypeOperatorOrHigher(type: TypeNode): TypeNode { + let postfixed: TypeNode; + switch (token()) { + case SyntaxKind.ExclamationToken: + postfixed = parsePostfixTypeOperator(type); // , SyntaxKind.ExclamationToken, 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..8d5c77afe5291 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -272,6 +272,7 @@ namespace ts { ExpressionWithTypeArguments, AsExpression, NonNullExpression, + NonNullTypeNode, MetaProperty, // Misc @@ -357,7 +358,6 @@ namespace ts { // The ? type JSDocUnknownType, JSDocNullableType, - JSDocNonNullableType, JSDocOptionalType, JSDocFunctionType, JSDocVariadicType, @@ -1545,6 +1545,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 { @@ -2055,11 +2060,6 @@ namespace ts { kind: SyntaxKind.JSDocUnknownType; } - export interface JSDocNonNullableType extends JSDocType { - kind: SyntaxKind.JSDocNonNullableType; - type: TypeNode; - } - export interface JSDocNullableType extends JSDocType { kind: SyntaxKind.JSDocNullableType; type: TypeNode; @@ -2079,7 +2079,7 @@ namespace ts { type: TypeNode; } - export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType; + export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType; export interface JSDoc extends Node { kind: SyntaxKind.JSDocComment; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f1a8dfd676b70..3fa6cdf2f278a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4539,10 +4539,6 @@ namespace ts { return node.kind === SyntaxKind.JSDocNullableType; } - export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType { - return node.kind === SyntaxKind.JSDocNonNullableType; - } - export function isJSDocOptionalType(node: Node): node is JSDocOptionalType { return node.kind === SyntaxKind.JSDocOptionalType; } 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/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json index 75bed7d45b640..2bf9b8e8d41be 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json @@ -1,5 +1,5 @@ { - "kind": "JSDocNonNullableType", + "kind": "NonNullTypeNode", "pos": 1, "end": 8, "flags": "JSDoc", diff --git a/tests/baselines/reference/nonNullType.js b/tests/baselines/reference/nonNullType.js new file mode 100644 index 0000000000000..8df6a54fbe072 --- /dev/null +++ b/tests/baselines/reference/nonNullType.js @@ -0,0 +1,9 @@ +//// [nonNullType.ts] +type z = string | undefined | null | never; +type a = string | undefined | null | never; +type b = a!; +type Assert = T!; +type c = Assert; + + +//// [nonNullType.js] diff --git a/tests/baselines/reference/nonNullType.symbols b/tests/baselines/reference/nonNullType.symbols new file mode 100644 index 0000000000000..ff8d534da59ad --- /dev/null +++ b/tests/baselines/reference/nonNullType.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/nonNullType.ts === +type z = string | undefined | null | never; +>z : Symbol(z, Decl(nonNullType.ts, 0, 0)) + +type a = string | undefined | null | never; +>a : Symbol(a, Decl(nonNullType.ts, 0, 43)) + +type b = a!; +>b : Symbol(b, Decl(nonNullType.ts, 1, 43)) +>a : Symbol(a, Decl(nonNullType.ts, 0, 43)) + +type Assert = T!; +>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12)) +>T : Symbol(T, Decl(nonNullType.ts, 3, 12)) +>T : Symbol(T, Decl(nonNullType.ts, 3, 12)) + +type c = Assert; +>c : Symbol(c, Decl(nonNullType.ts, 3, 20)) +>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12)) +>a : Symbol(a, Decl(nonNullType.ts, 0, 43)) + diff --git a/tests/baselines/reference/nonNullType.types b/tests/baselines/reference/nonNullType.types new file mode 100644 index 0000000000000..e45939e96566b --- /dev/null +++ b/tests/baselines/reference/nonNullType.types @@ -0,0 +1,23 @@ +=== tests/cases/compiler/nonNullType.ts === +type z = string | undefined | null | never; +>z : a +>null : null + +type a = string | undefined | null | never; +>a : a +>null : null + +type b = a!; +>b : string +>a : a + +type Assert = T!; +>Assert : T +>T : T +>T : T + +type c = Assert; +>c : a +>Assert : T +>a : a + diff --git a/tests/cases/compiler/nonNullType.ts b/tests/cases/compiler/nonNullType.ts new file mode 100644 index 0000000000000..a1c6cd76d07ef --- /dev/null +++ b/tests/cases/compiler/nonNullType.ts @@ -0,0 +1,6 @@ +// @strictNullChecks: true +type z = string | undefined | null | never; +type a = string | undefined | null | never; +type b = a!; +type Assert = T!; +type c = Assert;