Skip to content

Commit

Permalink
Merge pull request #5474 from Microsoft/forbid-this-as-constructor-pa…
Browse files Browse the repository at this point in the history
…rameter-type

Forbid this as constructor parameter type
  • Loading branch information
sandersn committed Nov 2, 2015
2 parents 5cbcafa + 67b9647 commit cc64210
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4420,7 +4420,8 @@ namespace ts {
let container = getThisContainer(node, /*includeArrowFunctions*/ false);
let parent = container && container.parent;
if (parent && (isClassLike(parent) || parent.kind === SyntaxKind.InterfaceDeclaration)) {
if (!(container.flags & NodeFlags.Static)) {
if (!(container.flags & NodeFlags.Static) &&
(container.kind !== SyntaxKind.Constructor || isNodeDescendentOf(node, (<ConstructorDeclaration>container).body))) {
return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType;
}
}
Expand Down
8 changes: 0 additions & 8 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,14 +359,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promi
sourceMaps: sourceMapDataList
};

function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
while (node) {
if (node === ancestor) return true;
node = node.parent;
}
return false;
}

function isUniqueLocalName(name: string, container: Node): boolean {
for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) {
if (node.locals && hasProperty(node.locals, name)) {
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,14 @@ namespace ts {
return !!node && (node.kind === SyntaxKind.ArrayBindingPattern || node.kind === SyntaxKind.ObjectBindingPattern);
}

export function isNodeDescendentOf(node: Node, ancestor: Node): boolean {
while (node) {
if (node === ancestor) return true;
node = node.parent;
}
return false;
}

export function isInAmbientContext(node: Node): boolean {
while (node) {
if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) {
Expand Down
5 changes: 4 additions & 1 deletion tests/baselines/reference/declarationFiles.errors.txt
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
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.
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.
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.
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.
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.


==== tests/cases/conformance/types/thisType/declarationFiles.ts (4 errors) ====
==== tests/cases/conformance/types/thisType/declarationFiles.ts (5 errors) ====

class C1 {
x: this;
f(x: this): this { return undefined; }
constructor(x: this) { }
~~~~
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
}

class C2 {
Expand Down
23 changes: 23 additions & 0 deletions tests/baselines/reference/thisTypeErrors2.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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.
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.


==== tests/cases/conformance/types/thisType/thisTypeErrors2.ts (2 errors) ====
class Base {
constructor(a: this) {
~~~~
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
}
}
class Generic<T> {
}
class Derived {
n: number;
constructor(public host: Generic<this>) {
~~~~
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
let self: this = this;
this.n = 12;
}
}

35 changes: 35 additions & 0 deletions tests/baselines/reference/thisTypeErrors2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//// [thisTypeErrors2.ts]
class Base {
constructor(a: this) {
}
}
class Generic<T> {
}
class Derived {
n: number;
constructor(public host: Generic<this>) {
let self: this = this;
this.n = 12;
}
}


//// [thisTypeErrors2.js]
var Base = (function () {
function Base(a) {
}
return Base;
})();
var Generic = (function () {
function Generic() {
}
return Generic;
})();
var Derived = (function () {
function Derived(host) {
this.host = host;
var self = this;
this.n = 12;
}
return Derived;
})();
57 changes: 57 additions & 0 deletions tests/baselines/reference/thisTypeInClasses.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
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.


==== tests/cases/conformance/types/thisType/thisTypeInClasses.ts (1 errors) ====
class C1 {
x: this;
f(x: this): this { return undefined; }
constructor(x: this) { }
~~~~
!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface.
}

class C2 {
[x: string]: this;
}

interface Foo<T> {
x: T;
y: this;
}

class C3 {
a: this[];
b: [this, this];
c: this | Date;
d: this & Date;
e: (((this)));
f: (x: this) => this;
g: new (x: this) => this;
h: Foo<this>;
i: Foo<this | (() => this)>;
j: (x: any) => x is this;
}

declare class C4 {
x: this;
f(x: this): this;
}

class C5 {
foo() {
let f1 = (x: this): this => this;
let f2 = (x: this) => this;
let f3 = (x: this) => (y: this) => this;
let f4 = (x: this) => {
let g = (y: this) => {
return () => this;
}
return g(this);
}
}
bar() {
let x1 = <this>undefined;
let x2 = undefined as this;
}
}

13 changes: 13 additions & 0 deletions tests/cases/conformance/types/thisType/thisTypeErrors2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class Base {
constructor(a: this) {
}
}
class Generic<T> {
}
class Derived {
n: number;
constructor(public host: Generic<this>) {
let self: this = this;
this.n = 12;
}
}

0 comments on commit cc64210

Please sign in to comment.