diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index dc1bab72c3..795fd199aa 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13121,8 +13121,10 @@ namespace ts { // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). const assumeInitialized = isParameter || isAlias || isOuterVariable || - type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || + type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || + isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || + declaration.kind === SyntaxKind.VariableDeclaration && (declaration).exclamationToken || declaration.flags & NodeFlags.Ambient; const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) : type === autoType || type === autoArrayType ? undefinedType : @@ -22669,6 +22671,7 @@ namespace ts { function isInstancePropertyWithoutInitializer(node: Node) { return node.kind === SyntaxKind.PropertyDeclaration && !hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract) && + !(node).exclamationToken && !(node).initializer; } @@ -26101,6 +26104,10 @@ namespace ts { } } + if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) { + return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); + } + if (compilerOptions.module !== ModuleKind.ES2015 && compilerOptions.module !== ModuleKind.ESNext && compilerOptions.module !== ModuleKind.System && !compilerOptions.noEmit && !(node.parent.parent.flags & NodeFlags.Ambient) && hasModifier(node.parent.parent, ModifierFlags.Export)) { checkESModuleMarker(node.name); @@ -26264,6 +26271,11 @@ namespace ts { if (node.flags & NodeFlags.Ambient && node.initializer) { return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } + + if (node.exclamationToken && (!isClassLike(node.parent) || !node.type || node.initializer || + node.flags & NodeFlags.Ambient || hasModifier(node, ModifierFlags.Static | ModifierFlags.Abstract))) { + return grammarErrorOnNode(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); + } } function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7c0336cf60..09319589da 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -831,6 +831,10 @@ "category": "Error", "code": 1254 }, + "A definite assignment assertion '!' is not permitted in this context.": { + "category": "Error", + "code": 1255 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", "code": 1300 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db2c09e34b..f9be81446f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -99,6 +99,7 @@ namespace ts { visitNode(cbNode, (node).dotDotDotToken) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).exclamationToken) || visitNode(cbNode, (node).type) || visitNode(cbNode, (node).initializer); case SyntaxKind.FunctionType: @@ -5254,6 +5255,9 @@ namespace ts { function parseVariableDeclaration(): VariableDeclaration { const node = createNode(SyntaxKind.VariableDeclaration); node.name = parseIdentifierOrPattern(); + if (node.name.kind === SyntaxKind.Identifier && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { + node.exclamationToken = parseTokenNode(); + } node.type = parseTypeAnnotation(); if (!isInOrOfKeyword(token())) { node.initializer = parseInitializer(); @@ -5346,6 +5350,9 @@ namespace ts { function parsePropertyDeclaration(node: PropertyDeclaration): PropertyDeclaration { node.kind = SyntaxKind.PropertyDeclaration; + if (!node.questionToken && token() === SyntaxKind.ExclamationToken && !scanner.hasPrecedingLineBreak()) { + node.exclamationToken = parseTokenNode(); + } node.type = parseTypeAnnotation(); // For instance properties specifically, since they are evaluated inside the constructor, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1ac7894b7a..53c36ac6ca 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -598,6 +598,7 @@ namespace ts { export type DotDotDotToken = Token; export type QuestionToken = Token; + export type ExclamationToken = Token; export type ColonToken = Token; export type EqualsToken = Token; export type AsteriskToken = Token; @@ -761,9 +762,10 @@ namespace ts { export interface VariableDeclaration extends NamedDeclaration { kind: SyntaxKind.VariableDeclaration; parent?: VariableDeclarationList | CatchClause; - name: BindingName; // Declared variable name - type?: TypeNode; // Optional type annotation - initializer?: Expression; // Optional initializer + name: BindingName; // Declared variable name + exclamationToken?: ExclamationToken; // Optional definite assignment assertion + type?: TypeNode; // Optional type annotation + initializer?: Expression; // Optional initializer } export interface VariableDeclarationList extends Node { @@ -801,8 +803,9 @@ namespace ts { export interface PropertyDeclaration extends ClassElement, JSDocContainer { kind: SyntaxKind.PropertyDeclaration; - questionToken?: QuestionToken; // Present for use with reporting a grammar error name: PropertyName; + questionToken?: QuestionToken; // Present for use with reporting a grammar error + exclamationToken?: ExclamationToken; type?: TypeNode; initializer?: Expression; // Optional initializer } @@ -860,6 +863,7 @@ namespace ts { dotDotDotToken?: DotDotDotToken; name: DeclarationName; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; type?: TypeNode; initializer?: Expression; }