Skip to content

Commit 1624e1b

Browse files
committed
Definite assignment assertion '!' on variable and property declarations
1 parent 87a8d41 commit 1624e1b

4 files changed

Lines changed: 32 additions & 5 deletions

File tree

src/compiler/checker.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13121,8 +13121,10 @@ namespace ts {
1312113121
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
1312213122
// declaration container are the same).
1312313123
const assumeInitialized = isParameter || isAlias || isOuterVariable ||
13124-
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
13124+
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 ||
13125+
isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
1312513126
node.parent.kind === SyntaxKind.NonNullExpression ||
13127+
declaration.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>declaration).exclamationToken ||
1312613128
declaration.flags & NodeFlags.Ambient;
1312713129
const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) :
1312813130
type === autoType || type === autoArrayType ? undefinedType :
@@ -22669,6 +22671,7 @@ namespace ts {
2266922671
function isInstancePropertyWithoutInitializer(node: Node) {
2267022672
return node.kind === SyntaxKind.PropertyDeclaration &&
2267122673
!hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) &&
22674+
!(<PropertyDeclaration>node).exclamationToken &&
2267222675
!(<PropertyDeclaration>node).initializer;
2267322676
}
2267422677

@@ -26101,6 +26104,10 @@ namespace ts {
2610126104
}
2610226105
}
2610326106

26107+
if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) {
26108+
return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context);
26109+
}
26110+
2610426111
if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ESNext && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit &&
2610526112
!(node.parent.parent.flags & NodeFlags.Ambient) && hasModifier(node.parent.parent, ModifierFlags.Export)) {
2610626113
checkESModuleMarker(node.name);
@@ -26264,6 +26271,11 @@ namespace ts {
2626426271
if (node.flags & NodeFlags.Ambient && node.initializer) {
2626526272
return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts);
2626626273
}
26274+
26275+
if (node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer ||
26276+
node.flags & NodeFlags.Ambient || hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) {
26277+
return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context);
26278+
}
2626726279
}
2626826280

2626926281
function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -831,6 +831,10 @@
831831
"category": "Error",
832832
"code": 1254
833833
},
834+
"A definite assignment assertion '!' is not permitted in this context.": {
835+
"category": "Error",
836+
"code": 1255
837+
},
834838
"'with' statements are not allowed in an async function block.": {
835839
"category": "Error",
836840
"code": 1300

src/compiler/parser.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ namespace ts {
9999
visitNode(cbNode, (<VariableLikeDeclaration>node).dotDotDotToken) ||
100100
visitNode(cbNode, (<VariableLikeDeclaration>node).name) ||
101101
visitNode(cbNode, (<VariableLikeDeclaration>node).questionToken) ||
102+
visitNode(cbNode, (<VariableLikeDeclaration>node).exclamationToken) ||
102103
visitNode(cbNode, (<VariableLikeDeclaration>node).type) ||
103104
visitNode(cbNode, (<VariableLikeDeclaration>node).initializer);
104105
case SyntaxKind.FunctionType:
@@ -5254,6 +5255,9 @@ namespace ts {
52545255
function parseVariableDeclaration(): VariableDeclaration {
52555256
const node = <VariableDeclaration>createNode(SyntaxKind.VariableDeclaration);
52565257
node.name = parseIdentifierOrPattern();
5258+
if (node.name.kind === SyntaxKind.Identifier && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
5259+
node.exclamationToken = parseTokenNode();
5260+
}
52575261
node.type = parseTypeAnnotation();
52585262
if (!isInOrOfKeyword(token())) {
52595263
node.initializer = parseInitializer();
@@ -5346,6 +5350,9 @@ namespace ts {
53465350

53475351
function parsePropertyDeclaration(node: PropertyDeclaration): PropertyDeclaration {
53485352
node.kind = SyntaxKind.PropertyDeclaration;
5353+
if (!node.questionToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) {
5354+
node.exclamationToken = parseTokenNode();
5355+
}
53495356
node.type = parseTypeAnnotation();
53505357

53515358
// For instance properties specifically, since they are evaluated inside the constructor,

src/compiler/types.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,7 @@ namespace ts {
598598

599599
export type DotDotDotToken = Token<SyntaxKind.DotDotDotToken>;
600600
export type QuestionToken = Token<SyntaxKind.QuestionToken>;
601+
export type ExclamationToken = Token<SyntaxKind.ExclamationToken>;
601602
export type ColonToken = Token<SyntaxKind.ColonToken>;
602603
export type EqualsToken = Token<SyntaxKind.EqualsToken>;
603604
export type AsteriskToken = Token<SyntaxKind.AsteriskToken>;
@@ -761,9 +762,10 @@ namespace ts {
761762
export interface VariableDeclaration extends NamedDeclaration {
762763
kind: SyntaxKind.VariableDeclaration;
763764
parent?: VariableDeclarationList | CatchClause;
764-
name: BindingName; // Declared variable name
765-
type?: TypeNode; // Optional type annotation
766-
initializer?: Expression; // Optional initializer
765+
name: BindingName; // Declared variable name
766+
exclamationToken?: ExclamationToken; // Optional definite assignment assertion
767+
type?: TypeNode; // Optional type annotation
768+
initializer?: Expression; // Optional initializer
767769
}
768770

769771
export interface VariableDeclarationList extends Node {
@@ -801,8 +803,9 @@ namespace ts {
801803

802804
export interface PropertyDeclaration extends ClassElement, JSDocContainer {
803805
kind: SyntaxKind.PropertyDeclaration;
804-
questionToken?: QuestionToken; // Present for use with reporting a grammar error
805806
name: PropertyName;
807+
questionToken?: QuestionToken; // Present for use with reporting a grammar error
808+
exclamationToken?: ExclamationToken;
806809
type?: TypeNode;
807810
initializer?: Expression; // Optional initializer
808811
}
@@ -860,6 +863,7 @@ namespace ts {
860863
dotDotDotToken?: DotDotDotToken;
861864
name: DeclarationName;
862865
questionToken?: QuestionToken;
866+
exclamationToken?: ExclamationToken;
863867
type?: TypeNode;
864868
initializer?: Expression;
865869
}

0 commit comments

Comments
 (0)