Skip to content

Commit dc4e63f

Browse files
type-level non-null operator (!)
1 parent a136f55 commit dc4e63f

12 files changed

+127
-24
lines changed

src/compiler/checker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -7540,6 +7540,10 @@ namespace ts {
75407540
return links.resolvedType;
75417541
}
75427542

7543+
function getTypeFromPostfixedTypeOperatorNode(node: NonNullTypeNode) { // TypeOperatorNode
7544+
return getNonNullableType(getTypeFromTypeNode(node.type));
7545+
}
7546+
75437547
function createIndexedAccessType(objectType: Type, indexType: Type) {
75447548
const type = <IndexedAccessType>createType(TypeFlags.IndexedAccess);
75457549
type.objectType = objectType;
@@ -7985,7 +7989,6 @@ namespace ts {
79857989
case SyntaxKind.JSDocNullableType:
79867990
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
79877991
case SyntaxKind.ParenthesizedType:
7988-
case SyntaxKind.JSDocNonNullableType:
79897992
case SyntaxKind.JSDocOptionalType:
79907993
case SyntaxKind.JSDocTypeExpression:
79917994
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
@@ -7997,6 +8000,8 @@ namespace ts {
79978000
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
79988001
case SyntaxKind.TypeOperator:
79998002
return getTypeFromTypeOperatorNode(<TypeOperatorNode>node);
8003+
case SyntaxKind.NonNullTypeNode:
8004+
return getTypeFromPostfixedTypeOperatorNode(<NonNullTypeNode>node);
80008005
case SyntaxKind.IndexedAccessType:
80018006
return getTypeFromIndexedAccessTypeNode(<IndexedAccessTypeNode>node);
80028007
case SyntaxKind.MappedType:
@@ -18701,6 +18706,7 @@ namespace ts {
1870118706
}
1870218707

1870318708
function checkTypeReferenceNode(node: TypeReferenceNode | ExpressionWithTypeArguments) {
18709+
// if (allowSyntheticDefaultImports) console.trace("checkTypeReferenceNode");
1870418710
checkGrammarTypeArguments(node, node.typeArguments);
1870518711
if (node.kind === SyntaxKind.TypeReference && node.typeName.jsdocDotPos !== undefined && !isInJavaScriptFile(node) && !isInJSDoc(node)) {
1870618712
grammarErrorAtPos(node, node.typeName.jsdocDotPos, 1, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
@@ -22310,7 +22316,6 @@ namespace ts {
2231022316
checkSignatureDeclaration(node as JSDocFunctionType);
2231122317
// falls through
2231222318
case SyntaxKind.JSDocVariadicType:
22313-
case SyntaxKind.JSDocNonNullableType:
2231422319
case SyntaxKind.JSDocNullableType:
2231522320
case SyntaxKind.JSDocAllType:
2231622321
case SyntaxKind.JSDocUnknownType:

src/compiler/emitter.ts

+2
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,8 @@ namespace ts {
797797
return emitAsExpression(<AsExpression>node);
798798
case SyntaxKind.NonNullExpression:
799799
return emitNonNullExpression(<NonNullExpression>node);
800+
case SyntaxKind.NonNullTypeNode:
801+
return emitWithSuffix((<NonNullTypeNode>node).type, "!");
800802
case SyntaxKind.MetaProperty:
801803
return emitMetaProperty(<MetaProperty>node);
802804

src/compiler/factory.ts

+12
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,18 @@ namespace ts {
12611261
: node;
12621262
}
12631263

1264+
export function createNonNullTypeNode(type: TypeNode) {
1265+
const node = <NonNullTypeNode>createSynthesizedNode(SyntaxKind.NonNullTypeNode);
1266+
node.type = parenthesizeElementTypeMember(type);
1267+
return node;
1268+
}
1269+
1270+
export function updateNonNullTypeNode(node: NonNullTypeNode, type: TypeNode) {
1271+
return node.type !== type
1272+
? updateNode(createNonNullTypeNode(type), node)
1273+
: node;
1274+
}
1275+
12641276
export function createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier) {
12651277
const node = <MetaProperty>createSynthesizedNode(SyntaxKind.MetaProperty);
12661278
node.keywordToken = keywordToken;

src/compiler/parser.ts

+35-10
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ namespace ts {
207207
visitNode(cbNode, (<AsExpression>node).type);
208208
case SyntaxKind.NonNullExpression:
209209
return visitNode(cbNode, (<NonNullExpression>node).expression);
210+
case SyntaxKind.NonNullTypeNode:
211+
return visitNode(cbNode, (<NonNullTypeNode>node).type);
210212
case SyntaxKind.MetaProperty:
211213
return visitNode(cbNode, (<MetaProperty>node).name);
212214
case SyntaxKind.ConditionalExpression:
@@ -396,8 +398,6 @@ namespace ts {
396398

397399
case SyntaxKind.JSDocTypeExpression:
398400
return visitNode(cbNode, (<JSDocTypeExpression>node).type);
399-
case SyntaxKind.JSDocNonNullableType:
400-
return visitNode(cbNode, (<JSDocNonNullableType>node).type);
401401
case SyntaxKind.JSDocNullableType:
402402
return visitNode(cbNode, (<JSDocNullableType>node).type);
403403
case SyntaxKind.JSDocOptionalType:
@@ -2175,8 +2175,8 @@ namespace ts {
21752175
return finishNode(parameter);
21762176
}
21772177

2178-
function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType | SyntaxKind.JSDocNonNullableType): TypeNode {
2179-
const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType;
2178+
function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType): TypeNode {
2179+
const result = createNode(kind) as JSDocVariadicType;
21802180
nextToken();
21812181
result.type = parseType();
21822182
return finishNode(result);
@@ -2662,8 +2662,6 @@ namespace ts {
26622662
return parseJSDocFunctionType();
26632663
case SyntaxKind.DotDotDotToken:
26642664
return parseJSDocNodeWithType(SyntaxKind.JSDocVariadicType);
2665-
case SyntaxKind.ExclamationToken:
2666-
return parseJSDocNodeWithType(SyntaxKind.JSDocNonNullableType);
26672665
case SyntaxKind.StringLiteral:
26682666
case SyntaxKind.NumericLiteral:
26692667
case SyntaxKind.TrueKeyword:
@@ -2744,7 +2742,7 @@ namespace ts {
27442742
if (!kind) return type;
27452743
nextToken();
27462744

2747-
const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType;
2745+
const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNullableType;
27482746
postfix.type = type;
27492747
return finishNode(postfix);
27502748

@@ -2753,8 +2751,6 @@ namespace ts {
27532751
case SyntaxKind.EqualsToken:
27542752
// only parse postfix = inside jsdoc, because it's ambiguous elsewhere
27552753
return contextFlags & NodeFlags.JSDoc ? SyntaxKind.JSDocOptionalType : undefined;
2756-
case SyntaxKind.ExclamationToken:
2757-
return SyntaxKind.JSDocNonNullableType;
27582754
case SyntaxKind.QuestionToken:
27592755
return SyntaxKind.JSDocNullableType;
27602756
}
@@ -2794,7 +2790,36 @@ namespace ts {
27942790
case SyntaxKind.KeyOfKeyword:
27952791
return parseTypeOperator(SyntaxKind.KeyOfKeyword);
27962792
}
2797-
return parseArrayTypeOrHigher();
2793+
const type = parseArrayTypeOrHigher();
2794+
return parsePostfixTypeOperatorOrHigher(type);
2795+
}
2796+
2797+
function parsePostfixTypeOperator(type: TypeNode) { // , parseKind: SyntaxKind, nodeKind: SyntaxKind
2798+
const node = <NonNullTypeNode>createNode(SyntaxKind.NonNullTypeNode);
2799+
// const node = <NonNullTypeNode>createNode(nodeKind);
2800+
// parseExpected(operator);
2801+
parseExpected(SyntaxKind.ExclamationToken);
2802+
// parseExpected(parseKind);
2803+
// node.operator = operator;
2804+
node.type = type;
2805+
const finished = finishNode(node);
2806+
finished.pos = type.pos;
2807+
// finished.end = type.end + 1;
2808+
return finished;
2809+
}
2810+
2811+
function parsePostfixTypeOperatorOrHigher(type: TypeNode): TypeNode {
2812+
let postfixed: TypeNode;
2813+
switch (token()) {
2814+
case SyntaxKind.ExclamationToken:
2815+
postfixed = parsePostfixTypeOperator(type); // , SyntaxKind.ExclamationToken, SyntaxKind.NonNullTypeNode
2816+
}
2817+
if (postfixed) {
2818+
return parsePostfixTypeOperatorOrHigher(postfixed);
2819+
}
2820+
else {
2821+
return type;
2822+
}
27982823
}
27992824

28002825
function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode {

src/compiler/types.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ namespace ts {
272272
ExpressionWithTypeArguments,
273273
AsExpression,
274274
NonNullExpression,
275+
NonNullTypeNode,
275276
MetaProperty,
276277

277278
// Misc
@@ -357,7 +358,6 @@ namespace ts {
357358
// The ? type
358359
JSDocUnknownType,
359360
JSDocNullableType,
360-
JSDocNonNullableType,
361361
JSDocOptionalType,
362362
JSDocFunctionType,
363363
JSDocVariadicType,
@@ -1545,6 +1545,11 @@ namespace ts {
15451545
expression: Expression;
15461546
}
15471547

1548+
export interface NonNullTypeNode extends TypeNode {
1549+
kind: SyntaxKind.NonNullTypeNode;
1550+
type: TypeNode;
1551+
}
1552+
15481553
// NOTE: MetaProperty is really a MemberExpression, but we consider it a PrimaryExpression
15491554
// for the same reasons we treat NewExpression as a PrimaryExpression.
15501555
export interface MetaProperty extends PrimaryExpression {
@@ -2055,11 +2060,6 @@ namespace ts {
20552060
kind: SyntaxKind.JSDocUnknownType;
20562061
}
20572062

2058-
export interface JSDocNonNullableType extends JSDocType {
2059-
kind: SyntaxKind.JSDocNonNullableType;
2060-
type: TypeNode;
2061-
}
2062-
20632063
export interface JSDocNullableType extends JSDocType {
20642064
kind: SyntaxKind.JSDocNullableType;
20652065
type: TypeNode;
@@ -2079,7 +2079,7 @@ namespace ts {
20792079
type: TypeNode;
20802080
}
20812081

2082-
export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType | JSDocNonNullableType;
2082+
export type JSDocTypeReferencingNode = JSDocVariadicType | JSDocOptionalType | JSDocNullableType;
20832083

20842084
export interface JSDoc extends Node {
20852085
kind: SyntaxKind.JSDocComment;

src/compiler/utilities.ts

-4
Original file line numberDiff line numberDiff line change
@@ -4539,10 +4539,6 @@ namespace ts {
45394539
return node.kind === SyntaxKind.JSDocNullableType;
45404540
}
45414541

4542-
export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType {
4543-
return node.kind === SyntaxKind.JSDocNonNullableType;
4544-
}
4545-
45464542
export function isJSDocOptionalType(node: Node): node is JSDocOptionalType {
45474543
return node.kind === SyntaxKind.JSDocOptionalType;
45484544
}

src/compiler/visitor.ts

+4
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,10 @@ namespace ts {
562562
return updateNonNullExpression(<NonNullExpression>node,
563563
visitNode((<NonNullExpression>node).expression, visitor, isExpression));
564564

565+
case SyntaxKind.NonNullTypeNode:
566+
return updateNonNullTypeNode(<NonNullTypeNode>node,
567+
visitNode((<NonNullTypeNode>node).type, visitor, isTypeNode));
568+
565569
case SyntaxKind.MetaProperty:
566570
return updateMetaProperty(<MetaProperty>node,
567571
visitNode((<MetaProperty>node).name, visitor, isIdentifier));

tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"kind": "JSDocNonNullableType",
2+
"kind": "NonNullTypeNode",
33
"pos": 1,
44
"end": 8,
55
"flags": "JSDoc",
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [nonNullType.ts]
2+
type z = string | undefined | null | never;
3+
type a = string | undefined | null | never;
4+
type b = a!;
5+
type Assert<T> = T!;
6+
type c = Assert<a>;
7+
8+
9+
//// [nonNullType.js]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/nonNullType.ts ===
2+
type z = string | undefined | null | never;
3+
>z : Symbol(z, Decl(nonNullType.ts, 0, 0))
4+
5+
type a = string | undefined | null | never;
6+
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))
7+
8+
type b = a!;
9+
>b : Symbol(b, Decl(nonNullType.ts, 1, 43))
10+
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))
11+
12+
type Assert<T> = T!;
13+
>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12))
14+
>T : Symbol(T, Decl(nonNullType.ts, 3, 12))
15+
>T : Symbol(T, Decl(nonNullType.ts, 3, 12))
16+
17+
type c = Assert<a>;
18+
>c : Symbol(c, Decl(nonNullType.ts, 3, 20))
19+
>Assert : Symbol(Assert, Decl(nonNullType.ts, 2, 12))
20+
>a : Symbol(a, Decl(nonNullType.ts, 0, 43))
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/nonNullType.ts ===
2+
type z = string | undefined | null | never;
3+
>z : a
4+
>null : null
5+
6+
type a = string | undefined | null | never;
7+
>a : a
8+
>null : null
9+
10+
type b = a!;
11+
>b : string
12+
>a : a
13+
14+
type Assert<T> = T!;
15+
>Assert : T
16+
>T : T
17+
>T : T
18+
19+
type c = Assert<a>;
20+
>c : a
21+
>Assert : T
22+
>a : a
23+

tests/cases/compiler/nonNullType.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @strictNullChecks: true
2+
type z = string | undefined | null | never;
3+
type a = string | undefined | null | never;
4+
type b = a!;
5+
type Assert<T> = T!;
6+
type c = Assert<a>;

0 commit comments

Comments
 (0)