Error on this.xxx access of previously declared but uninitialized property (#38030)

* Error on this.xxx access of previously declared but uninitialized property

* Add tests

* Accept new baselines
This commit is contained in:
Anders Hejlsberg 2020-04-28 12:52:14 -07:00 committed by GitHub
parent 9d8a70c809
commit 16d2eb7075
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 431 additions and 2 deletions

View file

@ -1364,7 +1364,7 @@ namespace ts {
return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile);
}
if (declaration.pos <= usage.pos) {
if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) {
// declaration is before usage
if (declaration.kind === SyntaxKind.BindingElement) {
// still might be illegal if declaration and usage are both binding elements (eg var [a = b, b = b] = [1, 2])

View file

@ -2,10 +2,11 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(4,24): error TS2715: Abstr
tests/cases/compiler/abstractPropertyInConstructor.ts(7,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2729: Property 'prop' is used before its initialization.
tests/cases/compiler/abstractPropertyInConstructor.ts(39,22): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
==== tests/cases/compiler/abstractPropertyInConstructor.ts (5 errors) ====
==== tests/cases/compiler/abstractPropertyInConstructor.ts (6 errors) ====
abstract class AbstractClass {
constructor(str: string, other: AbstractClass) {
this.method(parseInt(str));
@ -39,6 +40,9 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(39,22): error TS2715: Abst
other = this.prop;
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
~~~~
!!! error TS2729: Property 'prop' is used before its initialization.
!!! related TS2728 tests/cases/compiler/abstractPropertyInConstructor.ts:20:14: 'prop' is declared here.
fn = () => this.prop;
method2() {

View file

@ -0,0 +1,44 @@
tests/cases/compiler/initializerWithThisPropertyAccess.ts(3,14): error TS2729: Property 'a' is used before its initialization.
tests/cases/compiler/initializerWithThisPropertyAccess.ts(24,29): error TS2729: Property 'bar' is used before its initialization.
==== tests/cases/compiler/initializerWithThisPropertyAccess.ts (2 errors) ====
class A {
a: number;
b = this.a; // Error
~
!!! error TS2729: Property 'a' is used before its initialization.
!!! related TS2728 tests/cases/compiler/initializerWithThisPropertyAccess.ts:2:5: 'a' is declared here.
c = () => this.a;
d = (new A()).a;
constructor() {
this.a = 1;
}
}
class B extends A {
x = this.a;
}
class C {
a!: number;
b = this.a;
}
// Repro from #37979
class Foo {
private bar: Bar;
readonly barProp = this.bar.prop;
~~~
!!! error TS2729: Property 'bar' is used before its initialization.
!!! related TS2728 tests/cases/compiler/initializerWithThisPropertyAccess.ts:23:13: 'bar' is declared here.
constructor() {
this.bar = new Bar();
}
}
class Bar {
readonly prop = false;
}

View file

@ -0,0 +1,114 @@
//// [initializerWithThisPropertyAccess.ts]
class A {
a: number;
b = this.a; // Error
c = () => this.a;
d = (new A()).a;
constructor() {
this.a = 1;
}
}
class B extends A {
x = this.a;
}
class C {
a!: number;
b = this.a;
}
// Repro from #37979
class Foo {
private bar: Bar;
readonly barProp = this.bar.prop;
constructor() {
this.bar = new Bar();
}
}
class Bar {
readonly prop = false;
}
//// [initializerWithThisPropertyAccess.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var A = /** @class */ (function () {
function A() {
var _this = this;
this.b = this.a; // Error
this.c = function () { return _this.a; };
this.d = (new A()).a;
this.a = 1;
}
return A;
}());
var B = /** @class */ (function (_super) {
__extends(B, _super);
function B() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this.x = _this.a;
return _this;
}
return B;
}(A));
var C = /** @class */ (function () {
function C() {
this.b = this.a;
}
return C;
}());
// Repro from #37979
var Foo = /** @class */ (function () {
function Foo() {
this.barProp = this.bar.prop;
this.bar = new Bar();
}
return Foo;
}());
var Bar = /** @class */ (function () {
function Bar() {
this.prop = false;
}
return Bar;
}());
//// [initializerWithThisPropertyAccess.d.ts]
declare class A {
a: number;
b: number;
c: () => number;
d: number;
constructor();
}
declare class B extends A {
x: number;
}
declare class C {
a: number;
b: number;
}
declare class Foo {
private bar;
readonly barProp = false;
constructor();
}
declare class Bar {
readonly prop = false;
}

View file

@ -0,0 +1,90 @@
=== tests/cases/compiler/initializerWithThisPropertyAccess.ts ===
class A {
>A : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
a: number;
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
b = this.a; // Error
>b : Symbol(A.b, Decl(initializerWithThisPropertyAccess.ts, 1, 14))
>this.a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
>this : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
c = () => this.a;
>c : Symbol(A.c, Decl(initializerWithThisPropertyAccess.ts, 2, 15))
>this.a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
>this : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
d = (new A()).a;
>d : Symbol(A.d, Decl(initializerWithThisPropertyAccess.ts, 3, 21))
>(new A()).a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
>A : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
constructor() {
this.a = 1;
>this.a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
>this : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
}
}
class B extends A {
>B : Symbol(B, Decl(initializerWithThisPropertyAccess.ts, 8, 1))
>A : Symbol(A, Decl(initializerWithThisPropertyAccess.ts, 0, 0))
x = this.a;
>x : Symbol(B.x, Decl(initializerWithThisPropertyAccess.ts, 10, 19))
>this.a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
>this : Symbol(B, Decl(initializerWithThisPropertyAccess.ts, 8, 1))
>a : Symbol(A.a, Decl(initializerWithThisPropertyAccess.ts, 0, 9))
}
class C {
>C : Symbol(C, Decl(initializerWithThisPropertyAccess.ts, 12, 1))
a!: number;
>a : Symbol(C.a, Decl(initializerWithThisPropertyAccess.ts, 14, 9))
b = this.a;
>b : Symbol(C.b, Decl(initializerWithThisPropertyAccess.ts, 15, 15))
>this.a : Symbol(C.a, Decl(initializerWithThisPropertyAccess.ts, 14, 9))
>this : Symbol(C, Decl(initializerWithThisPropertyAccess.ts, 12, 1))
>a : Symbol(C.a, Decl(initializerWithThisPropertyAccess.ts, 14, 9))
}
// Repro from #37979
class Foo {
>Foo : Symbol(Foo, Decl(initializerWithThisPropertyAccess.ts, 17, 1))
private bar: Bar;
>bar : Symbol(Foo.bar, Decl(initializerWithThisPropertyAccess.ts, 21, 11))
>Bar : Symbol(Bar, Decl(initializerWithThisPropertyAccess.ts, 27, 1))
readonly barProp = this.bar.prop;
>barProp : Symbol(Foo.barProp, Decl(initializerWithThisPropertyAccess.ts, 22, 21))
>this.bar.prop : Symbol(Bar.prop, Decl(initializerWithThisPropertyAccess.ts, 29, 11))
>this.bar : Symbol(Foo.bar, Decl(initializerWithThisPropertyAccess.ts, 21, 11))
>this : Symbol(Foo, Decl(initializerWithThisPropertyAccess.ts, 17, 1))
>bar : Symbol(Foo.bar, Decl(initializerWithThisPropertyAccess.ts, 21, 11))
>prop : Symbol(Bar.prop, Decl(initializerWithThisPropertyAccess.ts, 29, 11))
constructor() {
this.bar = new Bar();
>this.bar : Symbol(Foo.bar, Decl(initializerWithThisPropertyAccess.ts, 21, 11))
>this : Symbol(Foo, Decl(initializerWithThisPropertyAccess.ts, 17, 1))
>bar : Symbol(Foo.bar, Decl(initializerWithThisPropertyAccess.ts, 21, 11))
>Bar : Symbol(Bar, Decl(initializerWithThisPropertyAccess.ts, 27, 1))
}
}
class Bar {
>Bar : Symbol(Bar, Decl(initializerWithThisPropertyAccess.ts, 27, 1))
readonly prop = false;
>prop : Symbol(Bar.prop, Decl(initializerWithThisPropertyAccess.ts, 29, 11))
}

View file

@ -0,0 +1,97 @@
=== tests/cases/compiler/initializerWithThisPropertyAccess.ts ===
class A {
>A : A
a: number;
>a : number
b = this.a; // Error
>b : number
>this.a : number
>this : this
>a : number
c = () => this.a;
>c : () => number
>() => this.a : () => number
>this.a : number
>this : this
>a : number
d = (new A()).a;
>d : number
>(new A()).a : number
>(new A()) : A
>new A() : A
>A : typeof A
>a : number
constructor() {
this.a = 1;
>this.a = 1 : 1
>this.a : number
>this : this
>a : number
>1 : 1
}
}
class B extends A {
>B : B
>A : A
x = this.a;
>x : number
>this.a : number
>this : this
>a : number
}
class C {
>C : C
a!: number;
>a : number
b = this.a;
>b : number
>this.a : number
>this : this
>a : number
}
// Repro from #37979
class Foo {
>Foo : Foo
private bar: Bar;
>bar : Bar
readonly barProp = this.bar.prop;
>barProp : false
>this.bar.prop : false
>this.bar : Bar
>this : this
>bar : Bar
>prop : false
constructor() {
this.bar = new Bar();
>this.bar = new Bar() : Bar
>this.bar : Bar
>this : this
>bar : Bar
>new Bar() : Bar
>Bar : typeof Bar
}
}
class Bar {
>Bar : Bar
readonly prop = false;
>prop : false
>false : false
}

View file

@ -0,0 +1,45 @@
tests/cases/conformance/expressions/identifiers/scopeResolutionIdentifiers.ts(24,14): error TS2729: Property 's' is used before its initialization.
==== tests/cases/conformance/expressions/identifiers/scopeResolutionIdentifiers.ts (1 errors) ====
// EveryType used in a nested scope of a different EveryType with the same name, type of the identifier is the one defined in the inner scope
var s: string;
module M1 {
export var s: number;
var n = s;
var n: number;
}
module M2 {
var s: number;
var n = s;
var n: number;
}
function fn() {
var s: boolean;
var n = s;
var n: boolean;
}
class C {
s: Date;
n = this.s;
~
!!! error TS2729: Property 's' is used before its initialization.
!!! related TS2728 tests/cases/conformance/expressions/identifiers/scopeResolutionIdentifiers.ts:23:5: 's' is declared here.
x() {
var p = this.n;
var p: Date;
}
}
module M3 {
var s: any;
module M4 {
var n = s;
var n: any;
}
}

View file

@ -0,0 +1,35 @@
// @strict: true
// @declaration: true
class A {
a: number;
b = this.a; // Error
c = () => this.a;
d = (new A()).a;
constructor() {
this.a = 1;
}
}
class B extends A {
x = this.a;
}
class C {
a!: number;
b = this.a;
}
// Repro from #37979
class Foo {
private bar: Bar;
readonly barProp = this.bar.prop;
constructor() {
this.bar = new Bar();
}
}
class Bar {
readonly prop = false;
}