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
This commit is contained in:
Nathan Shively-Sanders
2020-01-10 10:50:05 -08:00
committed by GitHub
parent 357f715382
commit 13cddae3f7
6 changed files with 147 additions and 1 deletions

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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));

View File

@@ -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))
}
}

View File

@@ -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
}
}

View File

@@ -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
}
}