Skip to content

Commit

Permalink
Error on this.xxx access of previously declared but uninitialized pro…
Browse files Browse the repository at this point in the history
…perty (#38030)

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

* Add tests

* Accept new baselines
  • Loading branch information
ahejlsberg authored Apr 28, 2020
1 parent 9d8a70c commit 16d2eb7
Show file tree
Hide file tree
Showing 8 changed files with 431 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

114 changes: 114 additions & 0 deletions tests/baselines/reference/initializerWithThisPropertyAccess.js
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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))
}

97 changes: 97 additions & 0 deletions tests/baselines/reference/initializerWithThisPropertyAccess.types
Original file line number Diff line number Diff line change
@@ -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
}

Loading

0 comments on commit 16d2eb7

Please sign in to comment.