From 04a2ed6a7075eab3776b3a44ada09324a33b9d1c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 13 Nov 2019 17:23:28 -0800 Subject: [PATCH 1/2] Fix printing and emit for definite assignment assertions --- src/compiler/binder.ts | 2 +- src/compiler/emitter.ts | 1 + src/compiler/factory.ts | 20 +++++++++++++++++++ src/compiler/transformers/declarations.ts | 2 +- src/compiler/transformers/ts.ts | 3 ++- src/testRunner/unittests/printer.ts | 11 ++++++++++ .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ ...eCorrectly.definiteAssignmentAssertions.js | 4 ++++ 9 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/printerApi/printsFileCorrectly.definiteAssignmentAssertions.js diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 3372512e0bca1..e8e1b682aca0e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -3807,7 +3807,7 @@ namespace ts { } // Type annotations are TypeScript syntax. - if (node.type) { + if (node.type || node.exclamationToken) { transformFlags |= TransformFlags.AssertTypeScript; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index cbff1fb2512f7..843b803623074 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2741,6 +2741,7 @@ namespace ts { function emitVariableDeclaration(node: VariableDeclaration) { emit(node.name); + emit(node.exclamationToken); emitTypeAnnotation(node.type); emitInitializer(node.initializer, node.type ? node.type.end : node.name.end, node); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d54244c083260..ee4d295dcbbc8 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1936,6 +1936,7 @@ namespace ts { return createSynthesizedNode(SyntaxKind.DebuggerStatement); } + /* @deprecated Use createTypeScriptVariableDeclaration instead and handle definite assignment assertions */ export function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression) { const node = createSynthesizedNode(SyntaxKind.VariableDeclaration); node.name = asName(name); @@ -1944,6 +1945,7 @@ namespace ts { return node; } + /* @deprecated Use updateTypeScriptVariableDeclaration instead and handle definite assignment assertions */ export function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined) { return node.name !== name || node.type !== type @@ -1952,6 +1954,24 @@ namespace ts { : node; } + export function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression) { + const node = createSynthesizedNode(SyntaxKind.VariableDeclaration); + node.name = asName(name); + node.type = type; + node.initializer = initializer !== undefined ? parenthesizeExpressionForList(initializer) : undefined; + node.exclamationToken = exclaimationToken; + return node; + } + + export function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + return node.name !== name + || node.type !== type + || node.initializer !== initializer + || node.exclamationToken !== exclaimationToken + ? updateNode(createTypeScriptVariableDeclaration(name, exclaimationToken, type, initializer), node) + : node; + } + export function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags = NodeFlags.None) { const node = createSynthesizedNode(SyntaxKind.VariableDeclarationList); node.flags |= flags & NodeFlags.BlockScoped; diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 7746ad00b430e..e6413af8e85ea 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -937,7 +937,7 @@ namespace ts { } shouldEnterSuppressNewDiagnosticsContextContext = true; suppressNewDiagnosticContexts = true; // Variable declaration types also suppress new diagnostic contexts, provided the contexts wouldn't be made for binding pattern types - return cleanup(updateVariableDeclaration(input, input.name, ensureType(input, input.type), ensureNoInitializer(input))); + return cleanup(updateTypeScriptVariableDeclaration(input, input.name, /*exclaimationToken*/ undefined, ensureType(input, input.type), ensureNoInitializer(input))); } case SyntaxKind.TypeParameter: { if (isPrivateMethodTypeParameter(input) && (input.default || input.constraint)) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index d413a835d0d86..ccad86da3e7c4 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2188,9 +2188,10 @@ namespace ts { } function visitVariableDeclaration(node: VariableDeclaration) { - return updateVariableDeclaration( + return updateTypeScriptVariableDeclaration( node, visitNode(node.name, visitor, isBindingName), + /*exclaimationToken*/ undefined, /*type*/ undefined, visitNode(node.initializer, visitor, isExpression)); } diff --git a/src/testRunner/unittests/printer.ts b/src/testRunner/unittests/printer.ts index 1605e7edc6006..39c5503933ad1 100644 --- a/src/testRunner/unittests/printer.ts +++ b/src/testRunner/unittests/printer.ts @@ -67,6 +67,17 @@ namespace ts { `class A extends B implements C implements D {}`, ScriptTarget.ES2017 ))); + + // github #35093 + printsCorrectly("definiteAssignmentAssertions", {}, printer => printer.printFile(createSourceFile( + "source.ts", + `class A { + prop!: string; + } + + let x!: string;`, + ScriptTarget.ES2017 + ))); }); describe("printBundle", () => { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 94c856cfd0401..ab3040f42c94c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4080,6 +4080,8 @@ declare namespace ts { function createDebuggerStatement(): DebuggerStatement; function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression): VariableDeclaration; function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; + function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression): VariableDeclaration; + function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags?: NodeFlags): VariableDeclarationList; function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList; function createFunctionDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined): FunctionDeclaration; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 433217ee422c7..3d6eb3e93b46c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4080,6 +4080,8 @@ declare namespace ts { function createDebuggerStatement(): DebuggerStatement; function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression): VariableDeclaration; function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; + function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression): VariableDeclaration; + function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags?: NodeFlags): VariableDeclarationList; function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList; function createFunctionDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined): FunctionDeclaration; diff --git a/tests/baselines/reference/printerApi/printsFileCorrectly.definiteAssignmentAssertions.js b/tests/baselines/reference/printerApi/printsFileCorrectly.definiteAssignmentAssertions.js new file mode 100644 index 0000000000000..46699aee0d1fb --- /dev/null +++ b/tests/baselines/reference/printerApi/printsFileCorrectly.definiteAssignmentAssertions.js @@ -0,0 +1,4 @@ +class A { + prop!: string; +} +let x!: string; From c88a9b8b10cac81de7a2fe21ec5e0723621fad49 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 21 Nov 2019 10:50:19 -0800 Subject: [PATCH 2/2] Make factories that handle definite assertions internal --- src/compiler/factory.ts | 6 ++++-- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 -- tests/baselines/reference/api/typescript.d.ts | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index ee4d295dcbbc8..12437d088fff6 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -1936,8 +1936,8 @@ namespace ts { return createSynthesizedNode(SyntaxKind.DebuggerStatement); } - /* @deprecated Use createTypeScriptVariableDeclaration instead and handle definite assignment assertions */ export function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression) { + /* Internally, one should probably use createTypeScriptVariableDeclaration instead and handle definite assignment assertions */ const node = createSynthesizedNode(SyntaxKind.VariableDeclaration); node.name = asName(name); node.type = type; @@ -1945,8 +1945,8 @@ namespace ts { return node; } - /* @deprecated Use updateTypeScriptVariableDeclaration instead and handle definite assignment assertions */ export function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined) { + /* Internally, one should probably use updateTypeScriptVariableDeclaration instead and handle definite assignment assertions */ return node.name !== name || node.type !== type || node.initializer !== initializer @@ -1954,6 +1954,7 @@ namespace ts { : node; } + /* @internal */ export function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression) { const node = createSynthesizedNode(SyntaxKind.VariableDeclaration); node.name = asName(name); @@ -1963,6 +1964,7 @@ namespace ts { return node; } + /* @internal */ export function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { return node.name !== name || node.type !== type diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ab3040f42c94c..94c856cfd0401 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4080,8 +4080,6 @@ declare namespace ts { function createDebuggerStatement(): DebuggerStatement; function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression): VariableDeclaration; function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; - function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression): VariableDeclaration; - function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags?: NodeFlags): VariableDeclarationList; function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList; function createFunctionDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined): FunctionDeclaration; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3d6eb3e93b46c..433217ee422c7 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4080,8 +4080,6 @@ declare namespace ts { function createDebuggerStatement(): DebuggerStatement; function createVariableDeclaration(name: string | BindingName, type?: TypeNode, initializer?: Expression): VariableDeclaration; function updateVariableDeclaration(node: VariableDeclaration, name: BindingName, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; - function createTypeScriptVariableDeclaration(name: string | BindingName, exclaimationToken?: Token, type?: TypeNode, initializer?: Expression): VariableDeclaration; - function updateTypeScriptVariableDeclaration(node: VariableDeclaration, name: BindingName, exclaimationToken: Token | undefined, type: TypeNode | undefined, initializer: Expression | undefined): VariableDeclaration; function createVariableDeclarationList(declarations: readonly VariableDeclaration[], flags?: NodeFlags): VariableDeclarationList; function updateVariableDeclarationList(node: VariableDeclarationList, declarations: readonly VariableDeclaration[]): VariableDeclarationList; function createFunctionDeclaration(decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, asteriskToken: AsteriskToken | undefined, name: string | Identifier | undefined, typeParameters: readonly TypeParameterDeclaration[] | undefined, parameters: readonly ParameterDeclaration[], type: TypeNode | undefined, body: Block | undefined): FunctionDeclaration;