From 13cddae3f7152d6f4aeae1aa71c0bc04abde8ad1 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 10 Jan 2020 10:50:05 -0800 Subject: [PATCH] Allow references to uninitialized ambient properties (#36112) Previously these were incorrectly treated just like normal properties: ```ts class Parent { a: any; constructor(arg: any) { this.a = arg; } } class Child extends Parent { declare a: number; constructor(arg: number) { super(arg); console.log(this.a); // Property 'a' is used before being assigned. (2565) } } ``` Fixes #35327 --- src/compiler/checker.ts | 2 +- ...ninitializedPropertyDeclaration.errors.txt | 15 +++++++ ...derivedUninitializedPropertyDeclaration.js | 31 +++++++++++++ ...edUninitializedPropertyDeclaration.symbols | 41 +++++++++++++++++ ...ivedUninitializedPropertyDeclaration.types | 44 +++++++++++++++++++ ...derivedUninitializedPropertyDeclaration.ts | 15 +++++++ 6 files changed, 147 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fbca0636b74..de07b62ac56 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23298,7 +23298,7 @@ namespace ts { const declaration = prop && prop.valueDeclaration; if (declaration && isInstancePropertyWithoutInitializer(declaration)) { const flowContainer = getControlFlowContainer(node); - if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent) { + if (flowContainer.kind === SyntaxKind.Constructor && flowContainer.parent === declaration.parent && !(declaration.flags & NodeFlags.Ambient)) { assumeUninitialized = true; } } diff --git a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.errors.txt b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.errors.txt index f657e335a48..b66987ac4fb 100644 --- a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.errors.txt +++ b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.errors.txt @@ -85,4 +85,19 @@ tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedP q!: 1 | 2 | 3 // ok, extends a property from an interface r!: 4 | 5 // error, from class } + + // #35327 + class L { + a: any; + constructor(arg: any) { + this.a = arg; + } + } + class M extends L { + declare a: number; + constructor(arg: number) { + super(arg); + console.log(this.a); // should be OK, M.a is ambient + } + } \ No newline at end of file diff --git a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.js b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.js index 48715eefe0c..a1536dafca4 100644 --- a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.js +++ b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.js @@ -65,6 +65,21 @@ class K extends J { q!: 1 | 2 | 3 // ok, extends a property from an interface r!: 4 | 5 // error, from class } + +// #35327 +class L { + a: any; + constructor(arg: any) { + this.a = arg; + } +} +class M extends L { + declare a: number; + constructor(arg: number) { + super(arg); + console.log(this.a); // should be OK, M.a is ambient + } +} //// [derivedUninitializedPropertyDeclaration.js] @@ -178,3 +193,19 @@ var K = /** @class */ (function (_super) { } return K; }(J)); +// #35327 +var L = /** @class */ (function () { + function L(arg) { + this.a = arg; + } + return L; +}()); +var M = /** @class */ (function (_super) { + __extends(M, _super); + function M(arg) { + var _this = _super.call(this, arg) || this; + console.log(_this.a); // should be OK, M.a is ambient + return _this; + } + return M; +}(L)); diff --git a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.symbols b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.symbols index 87e91288704..0b4bb459c77 100644 --- a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.symbols +++ b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.symbols @@ -147,3 +147,44 @@ class K extends J { >r : Symbol(K.r, Decl(derivedUninitializedPropertyDeclaration.ts, 63, 17)) } +// #35327 +class L { +>L : Symbol(L, Decl(derivedUninitializedPropertyDeclaration.ts, 65, 1)) + + a: any; +>a : Symbol(L.a, Decl(derivedUninitializedPropertyDeclaration.ts, 68, 9)) + + constructor(arg: any) { +>arg : Symbol(arg, Decl(derivedUninitializedPropertyDeclaration.ts, 70, 16)) + + this.a = arg; +>this.a : Symbol(L.a, Decl(derivedUninitializedPropertyDeclaration.ts, 68, 9)) +>this : Symbol(L, Decl(derivedUninitializedPropertyDeclaration.ts, 65, 1)) +>a : Symbol(L.a, Decl(derivedUninitializedPropertyDeclaration.ts, 68, 9)) +>arg : Symbol(arg, Decl(derivedUninitializedPropertyDeclaration.ts, 70, 16)) + } +} +class M extends L { +>M : Symbol(M, Decl(derivedUninitializedPropertyDeclaration.ts, 73, 1)) +>L : Symbol(L, Decl(derivedUninitializedPropertyDeclaration.ts, 65, 1)) + + declare a: number; +>a : Symbol(M.a, Decl(derivedUninitializedPropertyDeclaration.ts, 74, 19)) + + constructor(arg: number) { +>arg : Symbol(arg, Decl(derivedUninitializedPropertyDeclaration.ts, 76, 16)) + + super(arg); +>super : Symbol(L, Decl(derivedUninitializedPropertyDeclaration.ts, 65, 1)) +>arg : Symbol(arg, Decl(derivedUninitializedPropertyDeclaration.ts, 76, 16)) + + console.log(this.a); // should be OK, M.a is ambient +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>this.a : Symbol(M.a, Decl(derivedUninitializedPropertyDeclaration.ts, 74, 19)) +>this : Symbol(M, Decl(derivedUninitializedPropertyDeclaration.ts, 73, 1)) +>a : Symbol(M.a, Decl(derivedUninitializedPropertyDeclaration.ts, 74, 19)) + } +} + diff --git a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.types b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.types index d9c96f0c9fc..b85fe6b38e9 100644 --- a/tests/baselines/reference/derivedUninitializedPropertyDeclaration.types +++ b/tests/baselines/reference/derivedUninitializedPropertyDeclaration.types @@ -150,3 +150,47 @@ class K extends J { >r : 5 | 4 } +// #35327 +class L { +>L : L + + a: any; +>a : any + + constructor(arg: any) { +>arg : any + + this.a = arg; +>this.a = arg : any +>this.a : any +>this : this +>a : any +>arg : any + } +} +class M extends L { +>M : M +>L : L + + declare a: number; +>a : number + + constructor(arg: number) { +>arg : number + + super(arg); +>super(arg) : void +>super : typeof L +>arg : number + + console.log(this.a); // should be OK, M.a is ambient +>console.log(this.a) : void +>console.log : (message?: any, ...optionalParams: any[]) => void +>console : Console +>log : (message?: any, ...optionalParams: any[]) => void +>this.a : number +>this : this +>a : number + } +} + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedPropertyDeclaration.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedPropertyDeclaration.ts index e2d7494dd68..eab12031000 100644 --- a/tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedPropertyDeclaration.ts +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/derivedUninitializedPropertyDeclaration.ts @@ -65,3 +65,18 @@ class K extends J { q!: 1 | 2 | 3 // ok, extends a property from an interface r!: 4 | 5 // error, from class } + +// #35327 +class L { + a: any; + constructor(arg: any) { + this.a = arg; + } +} +class M extends L { + declare a: number; + constructor(arg: number) { + super(arg); + console.log(this.a); // should be OK, M.a is ambient + } +}