Skip to content

Commit cc64210

Browse files
committed
Merge pull request #5474 from Microsoft/forbid-this-as-constructor-parameter-type
Forbid this as constructor parameter type
2 parents 5cbcafa + 67b9647 commit cc64210

File tree

8 files changed

+142
-10
lines changed

8 files changed

+142
-10
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4420,7 +4420,8 @@ namespace ts {
44204420
let container = getThisContainer(node, /*includeArrowFunctions*/ false);
44214421
let parent = container && container.parent;
44224422
if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) {
4423-
if (!(container.flags & NodeFlags.Static)) {
4423+
if (!(container.flags & NodeFlags.Static) &&
4424+
(container.kind !== SyntaxKind.Constructor || isNodeDescendentOf(node, (<ConstructorDeclaration>container).body))) {
44244425
return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType;
44254426
}
44264427
}

src/compiler/emitter.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -359,14 +359,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
359359
sourceMaps: sourceMapDataList
360360
};
361361

362-
function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
363-
while (node) {
364-
if (node === ancestor) return true;
365-
node = node.parent;
366-
}
367-
return false;
368-
}
369-
370362
function isUniqueLocalName(name: string, container: Node): boolean {
371363
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
372364
if (node.locals && hasProperty(node.locals, name)) {

src/compiler/utilities.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,14 @@ namespace ts {
11671167
return !!node && (node.kind === SyntaxKind.ArrayBindingPattern || node.kind === SyntaxKind.ObjectBindingPattern);
11681168
}
11691169

1170+
export function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
1171+
while (node) {
1172+
if (node === ancestor) return true;
1173+
node = node.parent;
1174+
}
1175+
return false;
1176+
}
1177+
11701178
export function isInAmbientContext(node: Node): boolean {
11711179
while (node) {
11721180
if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) {

tests/baselines/reference/declarationFiles.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
1+
tests/cases/conformance/types/thisType/declarationFiles.ts(5,20): error TS2526: A 'this' type is available only in a non-static member of a class or interface.
12
tests/cases/conformance/types/thisType/declarationFiles.ts(31,5): error TS2527: The inferred type of 'x1' references an inaccessible 'this' type. A type annotation is necessary.
23
tests/cases/conformance/types/thisType/declarationFiles.ts(33,5): error TS2527: The inferred type of 'x3' references an inaccessible 'this' type. A type annotation is necessary.
34
tests/cases/conformance/types/thisType/declarationFiles.ts(35,5): error TS2527: The inferred type of 'f1' references an inaccessible 'this' type. A type annotation is necessary.
45
tests/cases/conformance/types/thisType/declarationFiles.ts(41,5): error TS2527: The inferred type of 'f3' references an inaccessible 'this' type. A type annotation is necessary.
56

67

7-
==== tests/cases/conformance/types/thisType/declarationFiles.ts (4 errors) ====
8+
==== tests/cases/conformance/types/thisType/declarationFiles.ts (5 errors) ====
89

910
class C1 {
1011
x: this;
1112
f(x: this): this { return undefined; }
1213
constructor(x: this) { }
14+
~~~~
15+
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
1316
}
1417

1518
class C2 {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
tests/cases/conformance/types/thisType/thisTypeErrors2.ts(2,20): error TS2526: A 'this' type is available only in a non-static member of a class or interface.
2+
tests/cases/conformance/types/thisType/thisTypeErrors2.ts(9,38): error TS2526: A 'this' type is available only in a non-static member of a class or interface.
3+
4+
5+
==== tests/cases/conformance/types/thisType/thisTypeErrors2.ts (2 errors) ====
6+
class Base {
7+
constructor(a: this) {
8+
~~~~
9+
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
10+
}
11+
}
12+
class Generic<T> {
13+
}
14+
class Derived {
15+
n: number;
16+
constructor(public host: Generic<this>) {
17+
~~~~
18+
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
19+
let self: this = this;
20+
this.n = 12;
21+
}
22+
}
23+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [thisTypeErrors2.ts]
2+
class Base {
3+
constructor(a: this) {
4+
}
5+
}
6+
class Generic<T> {
7+
}
8+
class Derived {
9+
n: number;
10+
constructor(public host: Generic<this>) {
11+
let self: this = this;
12+
this.n = 12;
13+
}
14+
}
15+
16+
17+
//// [thisTypeErrors2.js]
18+
var Base = (function () {
19+
function Base(a) {
20+
}
21+
return Base;
22+
})();
23+
var Generic = (function () {
24+
function Generic() {
25+
}
26+
return Generic;
27+
})();
28+
var Derived = (function () {
29+
function Derived(host) {
30+
this.host = host;
31+
var self = this;
32+
this.n = 12;
33+
}
34+
return Derived;
35+
})();
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
tests/cases/conformance/types/thisType/thisTypeInClasses.ts(4,20): error TS2526: A 'this' type is available only in a non-static member of a class or interface.
2+
3+
4+
==== tests/cases/conformance/types/thisType/thisTypeInClasses.ts (1 errors) ====
5+
class C1 {
6+
x: this;
7+
f(x: this): this { return undefined; }
8+
constructor(x: this) { }
9+
~~~~
10+
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
11+
}
12+
13+
class C2 {
14+
[x: string]: this;
15+
}
16+
17+
interface Foo<T> {
18+
x: T;
19+
y: this;
20+
}
21+
22+
class C3 {
23+
a: this[];
24+
b: [this, this];
25+
c: this | Date;
26+
d: this & Date;
27+
e: (((this)));
28+
f: (x: this) => this;
29+
g: new (x: this) => this;
30+
h: Foo<this>;
31+
i: Foo<this | (() => this)>;
32+
j: (x: any) => x is this;
33+
}
34+
35+
declare class C4 {
36+
x: this;
37+
f(x: this): this;
38+
}
39+
40+
class C5 {
41+
foo() {
42+
let f1 = (x: this): this => this;
43+
let f2 = (x: this) => this;
44+
let f3 = (x: this) => (y: this) => this;
45+
let f4 = (x: this) => {
46+
let g = (y: this) => {
47+
return () => this;
48+
}
49+
return g(this);
50+
}
51+
}
52+
bar() {
53+
let x1 = <this>undefined;
54+
let x2 = undefined as this;
55+
}
56+
}
57+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Base {
2+
constructor(a: this) {
3+
}
4+
}
5+
class Generic<T> {
6+
}
7+
class Derived {
8+
n: number;
9+
constructor(public host: Generic<this>) {
10+
let self: this = this;
11+
this.n = 12;
12+
}
13+
}

0 commit comments

Comments
 (0)