diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d1dbc9669529c..80bc58217aead 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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, (container).body))) { return getDeclaredTypeOfClassOrInterface(getSymbolOfNode(parent)).thisType; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b2fa553a73734..abcea979dd9e3 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -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)) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fd991039d3e1f..9b8948d6cc448 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -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)) { diff --git a/tests/baselines/reference/declarationFiles.errors.txt b/tests/baselines/reference/declarationFiles.errors.txt index c55cac9000b58..bbd65b6d6589c 100644 --- a/tests/baselines/reference/declarationFiles.errors.txt +++ b/tests/baselines/reference/declarationFiles.errors.txt @@ -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 { diff --git a/tests/baselines/reference/thisTypeErrors2.errors.txt b/tests/baselines/reference/thisTypeErrors2.errors.txt new file mode 100644 index 0000000000000..783325f4ef3f9 --- /dev/null +++ b/tests/baselines/reference/thisTypeErrors2.errors.txt @@ -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 { + } + class Derived { + n: number; + constructor(public host: Generic) { + ~~~~ +!!! 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; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/thisTypeErrors2.js b/tests/baselines/reference/thisTypeErrors2.js new file mode 100644 index 0000000000000..f48d0874e53e4 --- /dev/null +++ b/tests/baselines/reference/thisTypeErrors2.js @@ -0,0 +1,35 @@ +//// [thisTypeErrors2.ts] +class Base { + constructor(a: this) { + } +} +class Generic { +} +class Derived { + n: number; + constructor(public host: Generic) { + 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; +})(); diff --git a/tests/baselines/reference/thisTypeInClasses.errors.txt b/tests/baselines/reference/thisTypeInClasses.errors.txt new file mode 100644 index 0000000000000..db2ae1b579386 --- /dev/null +++ b/tests/baselines/reference/thisTypeInClasses.errors.txt @@ -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 { + 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; + i: Foo 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 = undefined; + let x2 = undefined as this; + } + } + \ No newline at end of file diff --git a/tests/cases/conformance/types/thisType/thisTypeErrors2.ts b/tests/cases/conformance/types/thisType/thisTypeErrors2.ts new file mode 100644 index 0000000000000..d29e714c4d0e0 --- /dev/null +++ b/tests/cases/conformance/types/thisType/thisTypeErrors2.ts @@ -0,0 +1,13 @@ +class Base { + constructor(a: this) { + } +} +class Generic { +} +class Derived { + n: number; + constructor(public host: Generic) { + let self: this = this; + this.n = 12; + } +}