From f0cc28a35b7be9973bcea87372957be2bd503316 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Fri, 30 Apr 2021 19:32:37 +0800 Subject: [PATCH 1/5] allow `this` in typeQuery --- src/compiler/checker.ts | 6 +- ...zerReferencingConstructorLocals.errors.txt | 12 ++-- ...eferencingConstructorParameters.errors.txt | 5 +- ...erReferencingConstructorParameters.symbols | 2 + tests/baselines/reference/typeofThis.js | 41 +++++++++++++ tests/baselines/reference/typeofThis.symbols | 49 +++++++++++++++ tests/baselines/reference/typeofThis.types | 61 +++++++++++++++++++ .../specifyingTypes/typeQueries/typeofThis.ts | 19 ++++++ 8 files changed, 183 insertions(+), 12 deletions(-) create mode 100644 tests/baselines/reference/typeofThis.js create mode 100644 tests/baselines/reference/typeofThis.symbols create mode 100644 tests/baselines/reference/typeofThis.types create mode 100644 tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5b96853eedc63..a2bc9233c3d1b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13102,7 +13102,8 @@ namespace ts { // The expression is processed as an identifier expression (section 4.3) // or property access expression(section 4.10), // the widened type(section 3.9) of which becomes the result. - links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(checkExpression(node.exprName))); + const type = isThisIdentifier(node.exprName) ? getThisType(node.exprName) : checkExpression(node.exprName); + links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(type)); } return links.resolvedType; } @@ -27077,7 +27078,8 @@ namespace ts { } function checkQualifiedName(node: QualifiedName, checkMode: CheckMode | undefined) { - return checkPropertyAccessExpressionOrQualifiedName(node, node.left, checkNonNullExpression(node.left), node.right, checkMode); + const leftType = isTypeQueryNode(node.parent) && isThisIdentifier(node.left) ? checkNonNullType(getThisType(node.left), node.left) : checkNonNullExpression(node.left); + return checkPropertyAccessExpressionOrQualifiedName(node, node.left, leftType, node.right, checkMode); } function isMethodAccessForCall(node: Node) { diff --git a/tests/baselines/reference/initializerReferencingConstructorLocals.errors.txt b/tests/baselines/reference/initializerReferencingConstructorLocals.errors.txt index 146048ceb9388..201cb233bf911 100644 --- a/tests/baselines/reference/initializerReferencingConstructorLocals.errors.txt +++ b/tests/baselines/reference/initializerReferencingConstructorLocals.errors.txt @@ -1,12 +1,12 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(4,9): error TS2304: Cannot find name 'z'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(5,15): error TS2304: Cannot find name 'z'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(6,14): error TS2339: Property 'z' does not exist on type 'C'. -tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(7,15): error TS2304: Cannot find name 'this'. +tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(7,20): error TS2339: Property 'z' does not exist on type 'C'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(9,9): error TS2304: Cannot find name 'z'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(14,9): error TS2304: Cannot find name 'z'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(15,15): error TS2304: Cannot find name 'z'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(16,14): error TS2339: Property 'z' does not exist on type 'D'. -tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(17,15): error TS2304: Cannot find name 'this'. +tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(17,20): error TS2339: Property 'z' does not exist on type 'D'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(19,9): error TS2304: Cannot find name 'z'. @@ -24,8 +24,8 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin ~ !!! error TS2339: Property 'z' does not exist on type 'C'. d: typeof this.z; // error - ~~~~ -!!! error TS2304: Cannot find name 'this'. + ~ +!!! error TS2339: Property 'z' does not exist on type 'C'. constructor(x) { z = 1; ~ @@ -44,8 +44,8 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin ~ !!! error TS2339: Property 'z' does not exist on type 'D'. d: typeof this.z; // error - ~~~~ -!!! error TS2304: Cannot find name 'this'. + ~ +!!! error TS2339: Property 'z' does not exist on type 'D'. constructor(x: T) { z = 1; ~ diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt b/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt index 0c92d8c52a632..44d9ec6937d0e 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt @@ -2,11 +2,10 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts(5,15): error TS2304: Cannot find name 'x'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts(10,9): error TS2663: Cannot find name 'x'. Did you mean the instance member 'this.x'? tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts(11,15): error TS2304: Cannot find name 'x'. -tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts(17,15): error TS2304: Cannot find name 'this'. tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts(23,9): error TS2663: Cannot find name 'x'. Did you mean the instance member 'this.x'? -==== tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts (6 errors) ==== +==== tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts (5 errors) ==== // Initializer expressions for instance member variables are evaluated in the scope of the class constructor body but are not permitted to reference parameters or local variables of the constructor. class C { @@ -32,8 +31,6 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin class E { a = this.x; // ok b: typeof this.x; // error - ~~~~ -!!! error TS2304: Cannot find name 'this'. constructor(public x) { } } diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.symbols b/tests/baselines/reference/initializerReferencingConstructorParameters.symbols index dc09db63a30e9..8bf0e638eec51 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.symbols +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.symbols @@ -38,6 +38,8 @@ class E { b: typeof this.x; // error >b : Symbol(E.b, Decl(initializerReferencingConstructorParameters.ts, 15, 15)) +>this.x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) +>x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) constructor(public x) { } >x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) diff --git a/tests/baselines/reference/typeofThis.js b/tests/baselines/reference/typeofThis.js new file mode 100644 index 0000000000000..7376dc42032d2 --- /dev/null +++ b/tests/baselines/reference/typeofThis.js @@ -0,0 +1,41 @@ +//// [typeofThis.ts] +class Test { + data = {}; + constructor() { + var copy: typeof this.data = {}; + } +} + +class Test1 { + data = { foo: '' }; + ['this'] = ''; + constructor() { + var copy: typeof this.data = { foo: '' }; + + var self: typeof this = this; + self.data; + + var str: typeof this.this = ''; + } +} + + +//// [typeofThis.js] +var Test = /** @class */ (function () { + function Test() { + this.data = {}; + var copy = {}; + } + return Test; +}()); +var Test1 = /** @class */ (function () { + function Test1() { + this.data = { foo: '' }; + this['this'] = ''; + var copy = { foo: '' }; + var self = this; + self.data; + var str = ''; + } + return Test1; +}()); diff --git a/tests/baselines/reference/typeofThis.symbols b/tests/baselines/reference/typeofThis.symbols new file mode 100644 index 0000000000000..391c7fbab4925 --- /dev/null +++ b/tests/baselines/reference/typeofThis.symbols @@ -0,0 +1,49 @@ +=== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts === +class Test { +>Test : Symbol(Test, Decl(typeofThis.ts, 0, 0)) + + data = {}; +>data : Symbol(Test.data, Decl(typeofThis.ts, 0, 12)) + + constructor() { + var copy: typeof this.data = {}; +>copy : Symbol(copy, Decl(typeofThis.ts, 3, 11)) +>this.data : Symbol(Test.data, Decl(typeofThis.ts, 0, 12)) +>data : Symbol(Test.data, Decl(typeofThis.ts, 0, 12)) + } +} + +class Test1 { +>Test1 : Symbol(Test1, Decl(typeofThis.ts, 5, 1)) + + data = { foo: '' }; +>data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>foo : Symbol(foo, Decl(typeofThis.ts, 8, 12)) + + ['this'] = ''; +>['this'] : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) +>'this' : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) + + constructor() { + var copy: typeof this.data = { foo: '' }; +>copy : Symbol(copy, Decl(typeofThis.ts, 11, 11)) +>this.data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>foo : Symbol(foo, Decl(typeofThis.ts, 11, 38)) + + var self: typeof this = this; +>self : Symbol(self, Decl(typeofThis.ts, 13, 11)) +>this : Symbol(Test1, Decl(typeofThis.ts, 5, 1)) + + self.data; +>self.data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>self : Symbol(self, Decl(typeofThis.ts, 13, 11)) +>data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) + + var str: typeof this.this = ''; +>str : Symbol(str, Decl(typeofThis.ts, 16, 11)) +>this.this : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) +>this : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) + } +} + diff --git a/tests/baselines/reference/typeofThis.types b/tests/baselines/reference/typeofThis.types new file mode 100644 index 0000000000000..eab123a064903 --- /dev/null +++ b/tests/baselines/reference/typeofThis.types @@ -0,0 +1,61 @@ +=== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts === +class Test { +>Test : Test + + data = {}; +>data : {} +>{} : {} + + constructor() { + var copy: typeof this.data = {}; +>copy : {} +>this.data : {} +>this : any +>data : {} +>{} : {} + } +} + +class Test1 { +>Test1 : Test1 + + data = { foo: '' }; +>data : { foo: string; } +>{ foo: '' } : { foo: string; } +>foo : string +>'' : "" + + ['this'] = ''; +>['this'] : string +>'this' : "this" +>'' : "" + + constructor() { + var copy: typeof this.data = { foo: '' }; +>copy : { foo: string; } +>this.data : { foo: string; } +>this : any +>data : { foo: string; } +>{ foo: '' } : { foo: string; } +>foo : string +>'' : "" + + var self: typeof this = this; +>self : this +>this : error +>this : this + + self.data; +>self.data : { foo: string; } +>self : this +>data : { foo: string; } + + var str: typeof this.this = ''; +>str : string +>this.this : string +>this : any +>this : string +>'' : "" + } +} + diff --git a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts new file mode 100644 index 0000000000000..06a1d321a2ee4 --- /dev/null +++ b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts @@ -0,0 +1,19 @@ +class Test { + data = {}; + constructor() { + var copy: typeof this.data = {}; + } +} + +class Test1 { + data = { foo: '' }; + ['this'] = ''; + constructor() { + var copy: typeof this.data = { foo: '' }; + + var self: typeof this = this; + self.data; + + var str: typeof this.this = ''; + } +} From e863a9ced5a587be78c20c6389ec13879f5afee3 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Sat, 8 May 2021 23:05:08 +0800 Subject: [PATCH 2/5] add tests --- ...eferencingConstructorParameters.errors.txt | 2 +- ...ializerReferencingConstructorParameters.js | 2 +- ...erReferencingConstructorParameters.symbols | 2 +- ...izerReferencingConstructorParameters.types | 2 +- .../baselines/reference/typeofThis.errors.txt | 79 +++++++++++++ tests/baselines/reference/typeofThis.js | 73 ++++++++++++ tests/baselines/reference/typeofThis.symbols | 77 +++++++++++++ tests/baselines/reference/typeofThis.types | 109 +++++++++++++++++- .../typeofThisWithImplicitThis.errors.txt | 10 ++ .../reference/typeofThisWithImplicitThis.js | 10 ++ .../typeofThisWithImplicitThis.symbols | 8 ++ .../typeofThisWithImplicitThis.types | 12 ++ ...ializerReferencingConstructorParameters.ts | 2 +- .../specifyingTypes/typeQueries/typeofThis.ts | 40 +++++++ .../typeQueries/typeofThisWithImplicitThis.ts | 5 + 15 files changed, 427 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/typeofThis.errors.txt create mode 100644 tests/baselines/reference/typeofThisWithImplicitThis.errors.txt create mode 100644 tests/baselines/reference/typeofThisWithImplicitThis.js create mode 100644 tests/baselines/reference/typeofThisWithImplicitThis.symbols create mode 100644 tests/baselines/reference/typeofThisWithImplicitThis.types create mode 100644 tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt b/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt index 44d9ec6937d0e..fec17b9115c68 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.errors.txt @@ -30,7 +30,7 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin class E { a = this.x; // ok - b: typeof this.x; // error + b: typeof this.x; // ok constructor(public x) { } } diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.js b/tests/baselines/reference/initializerReferencingConstructorParameters.js index 01f80b8cf1aa4..3f139808e981f 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.js +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.js @@ -15,7 +15,7 @@ class D { class E { a = this.x; // ok - b: typeof this.x; // error + b: typeof this.x; // ok constructor(public x) { } } diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.symbols b/tests/baselines/reference/initializerReferencingConstructorParameters.symbols index 8bf0e638eec51..6e5a757226c0d 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.symbols +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.symbols @@ -36,7 +36,7 @@ class E { >this : Symbol(E, Decl(initializerReferencingConstructorParameters.ts, 12, 1)) >x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) - b: typeof this.x; // error + b: typeof this.x; // ok >b : Symbol(E.b, Decl(initializerReferencingConstructorParameters.ts, 15, 15)) >this.x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) >x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16)) diff --git a/tests/baselines/reference/initializerReferencingConstructorParameters.types b/tests/baselines/reference/initializerReferencingConstructorParameters.types index 340860b25236d..5e0b5dde9a101 100644 --- a/tests/baselines/reference/initializerReferencingConstructorParameters.types +++ b/tests/baselines/reference/initializerReferencingConstructorParameters.types @@ -40,7 +40,7 @@ class E { >this : this >x : any - b: typeof this.x; // error + b: typeof this.x; // ok >b : any >this.x : any >this : any diff --git a/tests/baselines/reference/typeofThis.errors.txt b/tests/baselines/reference/typeofThis.errors.txt new file mode 100644 index 0000000000000..2f605d683cbb0 --- /dev/null +++ b/tests/baselines/reference/typeofThis.errors.txt @@ -0,0 +1,79 @@ +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(23,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(27,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(31,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. + + +==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts (6 errors) ==== + class Test { + data = {}; + constructor() { + var copy: typeof this.data = {}; + } + } + + class Test1 { + data = { foo: '' }; + ['this'] = ''; + constructor() { + var copy: typeof this.data = { foo: '' }; + + var self: typeof this = this; + self.data; + + var str: typeof this.this = ''; + } + } + + + function Test2() { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + + function Test3(this: { no: number }) { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + + function Test4(this: { no: number } | undefined) { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + + class Test5 { + no = 1; + + f = () => { + let x: typeof this.no = 1; + let self: typeof this = this; + } + } + + namespace Test6 { + export let f = () => { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + } + + module Test7 { + export let f = () => { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + } + + const Test8 = () => { + let x: typeof this.no = 1; + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeofThis.js b/tests/baselines/reference/typeofThis.js index 7376dc42032d2..76abad2a5c6df 100644 --- a/tests/baselines/reference/typeofThis.js +++ b/tests/baselines/reference/typeofThis.js @@ -18,6 +18,44 @@ class Test1 { var str: typeof this.this = ''; } } + + +function Test2() { + let x: typeof this.no = 1; +} + +function Test3(this: { no: number }) { + let x: typeof this.no = 1; +} + +function Test4(this: { no: number } | undefined) { + let x: typeof this.no = 1; +} + +class Test5 { + no = 1; + + f = () => { + let x: typeof this.no = 1; + let self: typeof this = this; + } +} + +namespace Test6 { + export let f = () => { + let x: typeof this.no = 1; + } +} + +module Test7 { + export let f = () => { + let x: typeof this.no = 1; + } +} + +const Test8 = () => { + let x: typeof this.no = 1; +} //// [typeofThis.js] @@ -39,3 +77,38 @@ var Test1 = /** @class */ (function () { } return Test1; }()); +function Test2() { + var x = 1; +} +function Test3() { + var x = 1; +} +function Test4() { + var x = 1; +} +var Test5 = /** @class */ (function () { + function Test5() { + var _this = this; + this.no = 1; + this.f = function () { + var x = 1; + var self = _this; + }; + } + return Test5; +}()); +var Test6; +(function (Test6) { + Test6.f = function () { + var x = 1; + }; +})(Test6 || (Test6 = {})); +var Test7; +(function (Test7) { + Test7.f = function () { + var x = 1; + }; +})(Test7 || (Test7 = {})); +var Test8 = function () { + var x = 1; +}; diff --git a/tests/baselines/reference/typeofThis.symbols b/tests/baselines/reference/typeofThis.symbols index 391c7fbab4925..ba30afc17c673 100644 --- a/tests/baselines/reference/typeofThis.symbols +++ b/tests/baselines/reference/typeofThis.symbols @@ -47,3 +47,80 @@ class Test1 { } } + +function Test2() { +>Test2 : Symbol(Test2, Decl(typeofThis.ts, 18, 1)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 22, 7)) +} + +function Test3(this: { no: number }) { +>Test3 : Symbol(Test3, Decl(typeofThis.ts, 23, 1)) +>this : Symbol(this, Decl(typeofThis.ts, 25, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 25, 22)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 26, 7)) +>this : Symbol(this, Decl(typeofThis.ts, 25, 15)) +} + +function Test4(this: { no: number } | undefined) { +>Test4 : Symbol(Test4, Decl(typeofThis.ts, 27, 1)) +>this : Symbol(this, Decl(typeofThis.ts, 29, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 29, 22)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 30, 7)) +>this : Symbol(this, Decl(typeofThis.ts, 29, 15)) +} + +class Test5 { +>Test5 : Symbol(Test5, Decl(typeofThis.ts, 31, 1)) + + no = 1; +>no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) + + f = () => { +>f : Symbol(Test5.f, Decl(typeofThis.ts, 34, 11)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 37, 11)) +>this.no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) +>no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) + + let self: typeof this = this; +>self : Symbol(self, Decl(typeofThis.ts, 38, 11)) +>this : Symbol(Test5, Decl(typeofThis.ts, 31, 1)) + } +} + +namespace Test6 { +>Test6 : Symbol(Test6, Decl(typeofThis.ts, 40, 1)) + + export let f = () => { +>f : Symbol(f, Decl(typeofThis.ts, 43, 14)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 44, 11)) + } +} + +module Test7 { +>Test7 : Symbol(Test7, Decl(typeofThis.ts, 46, 1)) + + export let f = () => { +>f : Symbol(f, Decl(typeofThis.ts, 49, 14)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 50, 11)) + } +} + +const Test8 = () => { +>Test8 : Symbol(Test8, Decl(typeofThis.ts, 54, 5)) + + let x: typeof this.no = 1; +>x : Symbol(x, Decl(typeofThis.ts, 55, 7)) +} + diff --git a/tests/baselines/reference/typeofThis.types b/tests/baselines/reference/typeofThis.types index eab123a064903..5844105710d7f 100644 --- a/tests/baselines/reference/typeofThis.types +++ b/tests/baselines/reference/typeofThis.types @@ -42,7 +42,7 @@ class Test1 { var self: typeof this = this; >self : this ->this : error +>this : any >this : this self.data; @@ -59,3 +59,110 @@ class Test1 { } } + +function Test2() { +>Test2 : () => void + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : any +>no : any +>1 : 1 +} + +function Test3(this: { no: number }) { +>Test3 : (this: { no: number;}) => void +>this : { no: number; } +>no : number + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : { no: number; } +>no : any +>1 : 1 +} + +function Test4(this: { no: number } | undefined) { +>Test4 : (this: { no: number;} | undefined) => void +>this : { no: number; } +>no : number + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : { no: number; } +>no : any +>1 : 1 +} + +class Test5 { +>Test5 : Test5 + + no = 1; +>no : number +>1 : 1 + + f = () => { +>f : () => void +>() => { let x: typeof this.no = 1; let self: typeof this = this; } : () => void + + let x: typeof this.no = 1; +>x : number +>this.no : number +>this : any +>no : number +>1 : 1 + + let self: typeof this = this; +>self : this +>this : any +>this : this + } +} + +namespace Test6 { +>Test6 : typeof Test6 + + export let f = () => { +>f : () => void +>() => { let x: typeof this.no = 1; } : () => void + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : any +>no : any +>1 : 1 + } +} + +module Test7 { +>Test7 : typeof Test7 + + export let f = () => { +>f : () => void +>() => { let x: typeof this.no = 1; } : () => void + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : any +>no : any +>1 : 1 + } +} + +const Test8 = () => { +>Test8 : () => void +>() => { let x: typeof this.no = 1;} : () => void + + let x: typeof this.no = 1; +>x : any +>this.no : any +>this : any +>no : any +>1 : 1 +} + diff --git a/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt b/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt new file mode 100644 index 0000000000000..87f1100a30180 --- /dev/null +++ b/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt @@ -0,0 +1,10 @@ +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts(2,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. + + +==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts (1 errors) ==== + function Test1() { + let x: typeof this.no = 1 + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + } + \ No newline at end of file diff --git a/tests/baselines/reference/typeofThisWithImplicitThis.js b/tests/baselines/reference/typeofThisWithImplicitThis.js new file mode 100644 index 0000000000000..bee952f1a96cb --- /dev/null +++ b/tests/baselines/reference/typeofThisWithImplicitThis.js @@ -0,0 +1,10 @@ +//// [typeofThisWithImplicitThis.ts] +function Test1() { + let x: typeof this.no = 1 +} + + +//// [typeofThisWithImplicitThis.js] +function Test1() { + var x = 1; +} diff --git a/tests/baselines/reference/typeofThisWithImplicitThis.symbols b/tests/baselines/reference/typeofThisWithImplicitThis.symbols new file mode 100644 index 0000000000000..96b64c4bdeb10 --- /dev/null +++ b/tests/baselines/reference/typeofThisWithImplicitThis.symbols @@ -0,0 +1,8 @@ +=== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts === +function Test1() { +>Test1 : Symbol(Test1, Decl(typeofThisWithImplicitThis.ts, 0, 0)) + + let x: typeof this.no = 1 +>x : Symbol(x, Decl(typeofThisWithImplicitThis.ts, 1, 7)) +} + diff --git a/tests/baselines/reference/typeofThisWithImplicitThis.types b/tests/baselines/reference/typeofThisWithImplicitThis.types new file mode 100644 index 0000000000000..40cedb44a9f64 --- /dev/null +++ b/tests/baselines/reference/typeofThisWithImplicitThis.types @@ -0,0 +1,12 @@ +=== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts === +function Test1() { +>Test1 : () => void + + let x: typeof this.no = 1 +>x : any +>this.no : any +>this : any +>no : any +>1 : 1 +} + diff --git a/tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts b/tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts index 8da0675449828..988a45eda78df 100644 --- a/tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts +++ b/tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorParameters.ts @@ -14,7 +14,7 @@ class D { class E { a = this.x; // ok - b: typeof this.x; // error + b: typeof this.x; // ok constructor(public x) { } } diff --git a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts index 06a1d321a2ee4..29c474cd75228 100644 --- a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts +++ b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts @@ -1,3 +1,5 @@ +// @noImplicitThis: true + class Test { data = {}; constructor() { @@ -17,3 +19,41 @@ class Test1 { var str: typeof this.this = ''; } } + + +function Test2() { + let x: typeof this.no = 1; +} + +function Test3(this: { no: number }) { + let x: typeof this.no = 1; +} + +function Test4(this: { no: number } | undefined) { + let x: typeof this.no = 1; +} + +class Test5 { + no = 1; + + f = () => { + let x: typeof this.no = 1; + let self: typeof this = this; + } +} + +namespace Test6 { + export let f = () => { + let x: typeof this.no = 1; + } +} + +module Test7 { + export let f = () => { + let x: typeof this.no = 1; + } +} + +const Test8 = () => { + let x: typeof this.no = 1; +} diff --git a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts new file mode 100644 index 0000000000000..d99cceaa583fb --- /dev/null +++ b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts @@ -0,0 +1,5 @@ +// @noImplicitThis: false + +function Test1() { + let x: typeof this.no = 1 +} From 83120f46b3eebb8536a10804319c80d978997076 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Wed, 12 May 2021 21:30:12 +0800 Subject: [PATCH 3/5] get this type as expression --- src/compiler/checker.ts | 9 +- src/compiler/utilities.ts | 6 + .../baselines/reference/typeofThis.errors.txt | 66 +++++++--- tests/baselines/reference/typeofThis.js | 78 ++++++++++- tests/baselines/reference/typeofThis.symbols | 96 +++++++++++++- tests/baselines/reference/typeofThis.types | 121 ++++++++++++++++-- .../typeofThisWithImplicitThis.errors.txt | 10 -- .../specifyingTypes/typeQueries/typeofThis.ts | 37 +++++- 8 files changed, 371 insertions(+), 52 deletions(-) delete mode 100644 tests/baselines/reference/typeofThisWithImplicitThis.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a2bc9233c3d1b..4aa16116749cc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13102,7 +13102,7 @@ namespace ts { // The expression is processed as an identifier expression (section 4.3) // or property access expression(section 4.10), // the widened type(section 3.9) of which becomes the result. - const type = isThisIdentifier(node.exprName) ? getThisType(node.exprName) : checkExpression(node.exprName); + const type = isThisIdentifier(node.exprName) ? checkThisExpression(node.exprName) : checkExpression(node.exprName); links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(type)); } return links.resolvedType; @@ -21842,7 +21842,7 @@ namespace ts { && (source as MetaProperty).name.escapedText === (target as MetaProperty).name.escapedText; case SyntaxKind.Identifier: case SyntaxKind.PrivateIdentifier: - return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || + return isThisInTypeQuery(source) ? target.kind === SyntaxKind.ThisKeyword : target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) || (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) && getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target); case SyntaxKind.ThisKeyword: @@ -24349,6 +24349,7 @@ namespace ts { } function checkThisExpression(node: Node): Type { + const isNodeInTypeQuery = isInTypeQuery(node); // Stop at the first arrow function so that we can // tell whether 'this' needs to be captured. let container = getThisContainer(node, /* includeArrowFunctions */ true); @@ -24392,7 +24393,7 @@ namespace ts { } // When targeting es6, mark that we'll need to capture `this` in its lexically bound scope. - if (capturedByArrowFunction && languageVersion < ScriptTarget.ES2015) { + if (!isNodeInTypeQuery && capturedByArrowFunction && languageVersion < ScriptTarget.ES2015) { captureLexicalThis(node, container); } @@ -27078,7 +27079,7 @@ namespace ts { } function checkQualifiedName(node: QualifiedName, checkMode: CheckMode | undefined) { - const leftType = isTypeQueryNode(node.parent) && isThisIdentifier(node.left) ? checkNonNullType(getThisType(node.left), node.left) : checkNonNullExpression(node.left); + const leftType = isTypeQueryNode(node.parent) && isThisIdentifier(node.left) ? checkNonNullType(checkThisExpression(node.left), node.left) : checkNonNullExpression(node.left); return checkPropertyAccessExpressionOrQualifiedName(node, node.left, leftType, node.right, checkMode); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6a7e23775b712..6351b3ff341c4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4329,6 +4329,12 @@ namespace ts { return !!node && node.kind === SyntaxKind.Identifier && identifierIsThisKeyword(node as Identifier); } + export function isThisInTypeQuery(node: Node): boolean { + return isThisIdentifier(node) && + (node.parent.kind === SyntaxKind.TypeQuery || + (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).left === node)); + } + export function identifierIsThisKeyword(id: Identifier): boolean { return id.originalKeywordKind === SyntaxKind.ThisKeyword; } diff --git a/tests/baselines/reference/typeofThis.errors.txt b/tests/baselines/reference/typeofThis.errors.txt index 2f605d683cbb0..3116a925e81ab 100644 --- a/tests/baselines/reference/typeofThis.errors.txt +++ b/tests/baselines/reference/typeofThis.errors.txt @@ -1,9 +1,9 @@ -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(23,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(27,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(31,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2526: A 'this' type is available only in a non-static member of a class or interface. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2526: A 'this' type is available only in a non-static member of a class or interface. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(23,19): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2331: 'this' cannot be referenced in a module or namespace body. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2331: 'this' cannot be referenced in a module or namespace body. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): error TS7041: The containing arrow function captures the global value of 'this'. ==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts (6 errors) ==== @@ -31,27 +31,23 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): function Test2() { let x: typeof this.no = 1; ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. +!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. } function Test3(this: { no: number }) { let x: typeof this.no = 1; - ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. } function Test4(this: { no: number } | undefined) { let x: typeof this.no = 1; - ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. } class Test5 { no = 1; f = () => { + // should not capture this. let x: typeof this.no = 1; - let self: typeof this = this; } } @@ -59,7 +55,9 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): export let f = () => { let x: typeof this.no = 1; ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. +!!! error TS2331: 'this' cannot be referenced in a module or namespace body. + ~~~~ +!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. } } @@ -67,13 +65,49 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): export let f = () => { let x: typeof this.no = 1; ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. +!!! error TS2331: 'this' cannot be referenced in a module or namespace body. + ~~~~ +!!! error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. } } const Test8 = () => { let x: typeof this.no = 1; ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. +!!! error TS7041: The containing arrow function captures the global value of 'this'. } - \ No newline at end of file + + class Test9 { + no = 0; + this = 0; + + f() { + if (this instanceof Test9D1) { + const d1: typeof this = this; + d1.f1(); + } + + if (this instanceof Test9D2) { + const d2: typeof this = this; + d2.f2(); + } + } + + g() { + if (this.no === 1) { + const no: typeof this.no = this.no; + } + + if (this.this === 1) { + const no: typeof this.this = this.this; + } + } + } + + class Test9D1 { + f1() {} + } + + class Test9D2 { + f2() {} + } \ No newline at end of file diff --git a/tests/baselines/reference/typeofThis.js b/tests/baselines/reference/typeofThis.js index 76abad2a5c6df..bbd61f575eff4 100644 --- a/tests/baselines/reference/typeofThis.js +++ b/tests/baselines/reference/typeofThis.js @@ -36,8 +36,8 @@ class Test5 { no = 1; f = () => { + // should not capture this. let x: typeof this.no = 1; - let self: typeof this = this; } } @@ -56,7 +56,41 @@ module Test7 { const Test8 = () => { let x: typeof this.no = 1; } - + +class Test9 { + no = 0; + this = 0; + + f() { + if (this instanceof Test9D1) { + const d1: typeof this = this; + d1.f1(); + } + + if (this instanceof Test9D2) { + const d2: typeof this = this; + d2.f2(); + } + } + + g() { + if (this.no === 1) { + const no: typeof this.no = this.no; + } + + if (this.this === 1) { + const no: typeof this.this = this.this; + } + } +} + +class Test9D1 { + f1() {} +} + +class Test9D2 { + f2() {} +} //// [typeofThis.js] var Test = /** @class */ (function () { @@ -88,11 +122,10 @@ function Test4() { } var Test5 = /** @class */ (function () { function Test5() { - var _this = this; this.no = 1; this.f = function () { + // should not capture this. var x = 1; - var self = _this; }; } return Test5; @@ -112,3 +145,40 @@ var Test7; var Test8 = function () { var x = 1; }; +var Test9 = /** @class */ (function () { + function Test9() { + this.no = 0; + this["this"] = 0; + } + Test9.prototype.f = function () { + if (this instanceof Test9D1) { + var d1 = this; + d1.f1(); + } + if (this instanceof Test9D2) { + var d2 = this; + d2.f2(); + } + }; + Test9.prototype.g = function () { + if (this.no === 1) { + var no = this.no; + } + if (this["this"] === 1) { + var no = this["this"]; + } + }; + return Test9; +}()); +var Test9D1 = /** @class */ (function () { + function Test9D1() { + } + Test9D1.prototype.f1 = function () { }; + return Test9D1; +}()); +var Test9D2 = /** @class */ (function () { + function Test9D2() { + } + Test9D2.prototype.f2 = function () { }; + return Test9D2; +}()); diff --git a/tests/baselines/reference/typeofThis.symbols b/tests/baselines/reference/typeofThis.symbols index ba30afc17c673..ff36141dd59aa 100644 --- a/tests/baselines/reference/typeofThis.symbols +++ b/tests/baselines/reference/typeofThis.symbols @@ -62,7 +62,9 @@ function Test3(this: { no: number }) { let x: typeof this.no = 1; >x : Symbol(x, Decl(typeofThis.ts, 26, 7)) +>this.no : Symbol(no, Decl(typeofThis.ts, 25, 22)) >this : Symbol(this, Decl(typeofThis.ts, 25, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 25, 22)) } function Test4(this: { no: number } | undefined) { @@ -72,7 +74,9 @@ function Test4(this: { no: number } | undefined) { let x: typeof this.no = 1; >x : Symbol(x, Decl(typeofThis.ts, 30, 7)) +>this.no : Symbol(no, Decl(typeofThis.ts, 29, 22)) >this : Symbol(this, Decl(typeofThis.ts, 29, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 29, 22)) } class Test5 { @@ -84,14 +88,11 @@ class Test5 { f = () => { >f : Symbol(Test5.f, Decl(typeofThis.ts, 34, 11)) + // should not capture this. let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 37, 11)) +>x : Symbol(x, Decl(typeofThis.ts, 38, 11)) >this.no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) >no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) - - let self: typeof this = this; ->self : Symbol(self, Decl(typeofThis.ts, 38, 11)) ->this : Symbol(Test5, Decl(typeofThis.ts, 31, 1)) } } @@ -124,3 +125,88 @@ const Test8 = () => { >x : Symbol(x, Decl(typeofThis.ts, 55, 7)) } +class Test9 { +>Test9 : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) + + no = 0; +>no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) + + this = 0; +>this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) + + f() { +>f : Symbol(Test9.f, Decl(typeofThis.ts, 60, 13)) + + if (this instanceof Test9D1) { +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 83, 1)) + + const d1: typeof this = this; +>d1 : Symbol(d1, Decl(typeofThis.ts, 64, 17)) + + d1.f1(); +>d1.f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) +>d1 : Symbol(d1, Decl(typeofThis.ts, 64, 17)) +>f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) + } + + if (this instanceof Test9D2) { +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 87, 1)) + + const d2: typeof this = this; +>d2 : Symbol(d2, Decl(typeofThis.ts, 69, 17)) + + d2.f2(); +>d2.f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) +>d2 : Symbol(d2, Decl(typeofThis.ts, 69, 17)) +>f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) + } + } + + g() { +>g : Symbol(Test9.g, Decl(typeofThis.ts, 72, 5)) + + if (this.no === 1) { +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) + + const no: typeof this.no = this.no; +>no : Symbol(no, Decl(typeofThis.ts, 76, 17)) +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) + } + + if (this.this === 1) { +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) + + const no: typeof this.this = this.this; +>no : Symbol(no, Decl(typeofThis.ts, 80, 17)) +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) + } + } +} + +class Test9D1 { +>Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 83, 1)) + + f1() {} +>f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) +} + +class Test9D2 { +>Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 87, 1)) + + f2() {} +>f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) +} diff --git a/tests/baselines/reference/typeofThis.types b/tests/baselines/reference/typeofThis.types index 5844105710d7f..612e415ec7e40 100644 --- a/tests/baselines/reference/typeofThis.types +++ b/tests/baselines/reference/typeofThis.types @@ -77,10 +77,10 @@ function Test3(this: { no: number }) { >no : number let x: typeof this.no = 1; ->x : any ->this.no : any +>x : number +>this.no : number >this : { no: number; } ->no : any +>no : number >1 : 1 } @@ -90,10 +90,10 @@ function Test4(this: { no: number } | undefined) { >no : number let x: typeof this.no = 1; ->x : any ->this.no : any +>x : number +>this.no : number >this : { no: number; } ->no : any +>no : number >1 : 1 } @@ -106,19 +106,15 @@ class Test5 { f = () => { >f : () => void ->() => { let x: typeof this.no = 1; let self: typeof this = this; } : () => void +>() => { // should not capture this. let x: typeof this.no = 1; } : () => void + // should not capture this. let x: typeof this.no = 1; >x : number >this.no : number >this : any >no : number >1 : 1 - - let self: typeof this = this; ->self : this ->this : any ->this : this } } @@ -166,3 +162,104 @@ const Test8 = () => { >1 : 1 } +class Test9 { +>Test9 : Test9 + + no = 0; +>no : number +>0 : 0 + + this = 0; +>this : number +>0 : 0 + + f() { +>f : () => void + + if (this instanceof Test9D1) { +>this instanceof Test9D1 : boolean +>this : this +>Test9D1 : typeof Test9D1 + + const d1: typeof this = this; +>d1 : this & Test9D1 +>this : any +>this : this & Test9D1 + + d1.f1(); +>d1.f1() : void +>d1.f1 : () => void +>d1 : this & Test9D1 +>f1 : () => void + } + + if (this instanceof Test9D2) { +>this instanceof Test9D2 : boolean +>this : this +>Test9D2 : typeof Test9D2 + + const d2: typeof this = this; +>d2 : this & Test9D2 +>this : any +>this : this & Test9D2 + + d2.f2(); +>d2.f2() : void +>d2.f2 : () => void +>d2 : this & Test9D2 +>f2 : () => void + } + } + + g() { +>g : () => void + + if (this.no === 1) { +>this.no === 1 : boolean +>this.no : number +>this : this +>no : number +>1 : 1 + + const no: typeof this.no = this.no; +>no : 1 +>this.no : 1 +>this : any +>no : 1 +>this.no : 1 +>this : this +>no : 1 + } + + if (this.this === 1) { +>this.this === 1 : boolean +>this.this : number +>this : this +>this : number +>1 : 1 + + const no: typeof this.this = this.this; +>no : 1 +>this.this : 1 +>this : any +>this : 1 +>this.this : 1 +>this : this +>this : 1 + } + } +} + +class Test9D1 { +>Test9D1 : Test9D1 + + f1() {} +>f1 : () => void +} + +class Test9D2 { +>Test9D2 : Test9D2 + + f2() {} +>f2 : () => void +} diff --git a/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt b/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt deleted file mode 100644 index 87f1100a30180..0000000000000 --- a/tests/baselines/reference/typeofThisWithImplicitThis.errors.txt +++ /dev/null @@ -1,10 +0,0 @@ -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts(2,19): error TS2526: A 'this' type is available only in a non-static member of a class or interface. - - -==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThisWithImplicitThis.ts (1 errors) ==== - function Test1() { - let x: typeof this.no = 1 - ~~~~ -!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. - } - \ No newline at end of file diff --git a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts index 29c474cd75228..dac4959646507 100644 --- a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts +++ b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts @@ -37,8 +37,8 @@ class Test5 { no = 1; f = () => { + // should not capture this. let x: typeof this.no = 1; - let self: typeof this = this; } } @@ -57,3 +57,38 @@ module Test7 { const Test8 = () => { let x: typeof this.no = 1; } + +class Test9 { + no = 0; + this = 0; + + f() { + if (this instanceof Test9D1) { + const d1: typeof this = this; + d1.f1(); + } + + if (this instanceof Test9D2) { + const d2: typeof this = this; + d2.f2(); + } + } + + g() { + if (this.no === 1) { + const no: typeof this.no = this.no; + } + + if (this.this === 1) { + const no: typeof this.this = this.this; + } + } +} + +class Test9D1 { + f1() {} +} + +class Test9D2 { + f2() {} +} \ No newline at end of file From ed29f3872260084ed76f7dc86fa68b22727f67a5 Mon Sep 17 00:00:00 2001 From: Zzzen Date: Thu, 3 Jun 2021 22:01:14 +0800 Subject: [PATCH 4/5] handle nested nodes --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 12 +- .../baselines/reference/typeofThis.errors.txt | 50 +++- tests/baselines/reference/typeofThis.js | 59 +++++ tests/baselines/reference/typeofThis.symbols | 246 +++++++++++++----- tests/baselines/reference/typeofThis.types | 118 ++++++++- .../specifyingTypes/typeQueries/typeofThis.ts | 31 +++ 7 files changed, 433 insertions(+), 85 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0d46c4369e86e..1e0d0f177c20a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27257,7 +27257,7 @@ namespace ts { } function checkQualifiedName(node: QualifiedName, checkMode: CheckMode | undefined) { - const leftType = isTypeQueryNode(node.parent) && isThisIdentifier(node.left) ? checkNonNullType(checkThisExpression(node.left), node.left) : checkNonNullExpression(node.left); + const leftType = isPartOfTypeQuery(node) && isThisIdentifier(node.left) ? checkNonNullType(checkThisExpression(node.left), node.left) : checkNonNullExpression(node.left); return checkPropertyAccessExpressionOrQualifiedName(node, node.left, leftType, node.right, checkMode); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0918291656007..a5d2b45c4e392 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4349,9 +4349,15 @@ namespace ts { } export function isThisInTypeQuery(node: Node): boolean { - return isThisIdentifier(node) && - (node.parent.kind === SyntaxKind.TypeQuery || - (node.parent.kind === SyntaxKind.QualifiedName && (node.parent as QualifiedName).left === node)); + if (!isThisIdentifier(node)) { + return false; + } + + while (isQualifiedName(node.parent) && node.parent.left === node) { + node = node.parent; + } + + return node.parent.kind === SyntaxKind.TypeQuery; } export function identifierIsThisKeyword(id: Identifier): boolean { diff --git a/tests/baselines/reference/typeofThis.errors.txt b/tests/baselines/reference/typeofThis.errors.txt index 3116a925e81ab..0859ccaa2d7c0 100644 --- a/tests/baselines/reference/typeofThis.errors.txt +++ b/tests/baselines/reference/typeofThis.errors.txt @@ -1,12 +1,14 @@ -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(23,19): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2331: 'this' cannot be referenced in a module or namespace body. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(45,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2331: 'this' cannot be referenced in a module or namespace body. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(51,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. -tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): error TS7041: The containing arrow function captures the global value of 'this'. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(24,19): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(32,19): error TS2532: Object is possibly 'undefined'. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(46,23): error TS2331: 'this' cannot be referenced in a module or namespace body. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(46,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(52,23): error TS2331: 'this' cannot be referenced in a module or namespace body. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(52,23): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(57,19): error TS7041: The containing arrow function captures the global value of 'this'. +tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(57,24): error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature. -==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts (6 errors) ==== +==== tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts (8 errors) ==== class Test { data = {}; constructor() { @@ -19,6 +21,7 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): ['this'] = ''; constructor() { var copy: typeof this.data = { foo: '' }; + var foo: typeof this.data.foo = ''; var self: typeof this = this; self.data; @@ -40,6 +43,8 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): function Test4(this: { no: number } | undefined) { let x: typeof this.no = 1; + ~~~~ +!!! error TS2532: Object is possibly 'undefined'. } class Test5 { @@ -75,6 +80,8 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): let x: typeof this.no = 1; ~~~~ !!! error TS7041: The containing arrow function captures the global value of 'this'. + ~~ +!!! error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature. } class Test9 { @@ -110,4 +117,33 @@ tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts(56,19): class Test9D2 { f2() {} + } + + class Test10 { + a?: { b?: string } + + foo() { + let a: typeof this.a = undefined as any; + if (this.a) { + let a: typeof this.a = undefined as any; // should narrow to { b?: string } + let b: typeof this.a.b = undefined as any; + + if (this.a.b) { + let b: typeof this.a.b = undefined as any; // should narrow to string + } + } + } + } + + class Test11 { + this?: { x?: string }; + + foo() { + const o = this; + let bar: typeof o.this = {}; + + if (o.this && o.this.x) { + let y: string = o.this.x; // should narrow to string + } + } } \ No newline at end of file diff --git a/tests/baselines/reference/typeofThis.js b/tests/baselines/reference/typeofThis.js index bbd61f575eff4..e06e3e383f405 100644 --- a/tests/baselines/reference/typeofThis.js +++ b/tests/baselines/reference/typeofThis.js @@ -11,6 +11,7 @@ class Test1 { ['this'] = ''; constructor() { var copy: typeof this.data = { foo: '' }; + var foo: typeof this.data.foo = ''; var self: typeof this = this; self.data; @@ -90,9 +91,39 @@ class Test9D1 { class Test9D2 { f2() {} +} + +class Test10 { + a?: { b?: string } + + foo() { + let a: typeof this.a = undefined as any; + if (this.a) { + let a: typeof this.a = undefined as any; // should narrow to { b?: string } + let b: typeof this.a.b = undefined as any; + + if (this.a.b) { + let b: typeof this.a.b = undefined as any; // should narrow to string + } + } + } +} + +class Test11 { + this?: { x?: string }; + + foo() { + const o = this; + let bar: typeof o.this = {}; + + if (o.this && o.this.x) { + let y: string = o.this.x; // should narrow to string + } + } } //// [typeofThis.js] +"use strict"; var Test = /** @class */ (function () { function Test() { this.data = {}; @@ -105,6 +136,7 @@ var Test1 = /** @class */ (function () { this.data = { foo: '' }; this['this'] = ''; var copy = { foo: '' }; + var foo = ''; var self = this; self.data; var str = ''; @@ -182,3 +214,30 @@ var Test9D2 = /** @class */ (function () { Test9D2.prototype.f2 = function () { }; return Test9D2; }()); +var Test10 = /** @class */ (function () { + function Test10() { + } + Test10.prototype.foo = function () { + var a = undefined; + if (this.a) { + var a_1 = undefined; // should narrow to { b?: string } + var b = undefined; + if (this.a.b) { + var b_1 = undefined; // should narrow to string + } + } + }; + return Test10; +}()); +var Test11 = /** @class */ (function () { + function Test11() { + } + Test11.prototype.foo = function () { + var o = this; + var bar = {}; + if (o["this"] && o["this"].x) { + var y = o["this"].x; // should narrow to string + } + }; + return Test11; +}()); diff --git a/tests/baselines/reference/typeofThis.symbols b/tests/baselines/reference/typeofThis.symbols index ff36141dd59aa..dde439e3d0d96 100644 --- a/tests/baselines/reference/typeofThis.symbols +++ b/tests/baselines/reference/typeofThis.symbols @@ -31,17 +31,24 @@ class Test1 { >data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) >foo : Symbol(foo, Decl(typeofThis.ts, 11, 38)) + var foo: typeof this.data.foo = ''; +>foo : Symbol(foo, Decl(typeofThis.ts, 12, 11)) +>this.data.foo : Symbol(foo, Decl(typeofThis.ts, 8, 12)) +>this.data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) +>foo : Symbol(foo, Decl(typeofThis.ts, 8, 12)) + var self: typeof this = this; ->self : Symbol(self, Decl(typeofThis.ts, 13, 11)) +>self : Symbol(self, Decl(typeofThis.ts, 14, 11)) >this : Symbol(Test1, Decl(typeofThis.ts, 5, 1)) self.data; >self.data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) ->self : Symbol(self, Decl(typeofThis.ts, 13, 11)) +>self : Symbol(self, Decl(typeofThis.ts, 14, 11)) >data : Symbol(Test1.data, Decl(typeofThis.ts, 7, 13)) var str: typeof this.this = ''; ->str : Symbol(str, Decl(typeofThis.ts, 16, 11)) +>str : Symbol(str, Decl(typeofThis.ts, 17, 11)) >this.this : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) >this : Symbol(Test1['this'], Decl(typeofThis.ts, 8, 23)) } @@ -49,164 +56,259 @@ class Test1 { function Test2() { ->Test2 : Symbol(Test2, Decl(typeofThis.ts, 18, 1)) +>Test2 : Symbol(Test2, Decl(typeofThis.ts, 19, 1)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 22, 7)) +>x : Symbol(x, Decl(typeofThis.ts, 23, 7)) } function Test3(this: { no: number }) { ->Test3 : Symbol(Test3, Decl(typeofThis.ts, 23, 1)) ->this : Symbol(this, Decl(typeofThis.ts, 25, 15)) ->no : Symbol(no, Decl(typeofThis.ts, 25, 22)) +>Test3 : Symbol(Test3, Decl(typeofThis.ts, 24, 1)) +>this : Symbol(this, Decl(typeofThis.ts, 26, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 26, 22)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 26, 7)) ->this.no : Symbol(no, Decl(typeofThis.ts, 25, 22)) ->this : Symbol(this, Decl(typeofThis.ts, 25, 15)) ->no : Symbol(no, Decl(typeofThis.ts, 25, 22)) +>x : Symbol(x, Decl(typeofThis.ts, 27, 7)) +>this.no : Symbol(no, Decl(typeofThis.ts, 26, 22)) +>this : Symbol(this, Decl(typeofThis.ts, 26, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 26, 22)) } function Test4(this: { no: number } | undefined) { ->Test4 : Symbol(Test4, Decl(typeofThis.ts, 27, 1)) ->this : Symbol(this, Decl(typeofThis.ts, 29, 15)) ->no : Symbol(no, Decl(typeofThis.ts, 29, 22)) +>Test4 : Symbol(Test4, Decl(typeofThis.ts, 28, 1)) +>this : Symbol(this, Decl(typeofThis.ts, 30, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 30, 22)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 30, 7)) ->this.no : Symbol(no, Decl(typeofThis.ts, 29, 22)) ->this : Symbol(this, Decl(typeofThis.ts, 29, 15)) ->no : Symbol(no, Decl(typeofThis.ts, 29, 22)) +>x : Symbol(x, Decl(typeofThis.ts, 31, 7)) +>this.no : Symbol(no, Decl(typeofThis.ts, 30, 22)) +>this : Symbol(this, Decl(typeofThis.ts, 30, 15)) +>no : Symbol(no, Decl(typeofThis.ts, 30, 22)) } class Test5 { ->Test5 : Symbol(Test5, Decl(typeofThis.ts, 31, 1)) +>Test5 : Symbol(Test5, Decl(typeofThis.ts, 32, 1)) no = 1; ->no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) +>no : Symbol(Test5.no, Decl(typeofThis.ts, 34, 13)) f = () => { ->f : Symbol(Test5.f, Decl(typeofThis.ts, 34, 11)) +>f : Symbol(Test5.f, Decl(typeofThis.ts, 35, 11)) // should not capture this. let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 38, 11)) ->this.no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) ->no : Symbol(Test5.no, Decl(typeofThis.ts, 33, 13)) +>x : Symbol(x, Decl(typeofThis.ts, 39, 11)) +>this.no : Symbol(Test5.no, Decl(typeofThis.ts, 34, 13)) +>no : Symbol(Test5.no, Decl(typeofThis.ts, 34, 13)) } } namespace Test6 { ->Test6 : Symbol(Test6, Decl(typeofThis.ts, 40, 1)) +>Test6 : Symbol(Test6, Decl(typeofThis.ts, 41, 1)) export let f = () => { ->f : Symbol(f, Decl(typeofThis.ts, 43, 14)) +>f : Symbol(f, Decl(typeofThis.ts, 44, 14)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 44, 11)) +>x : Symbol(x, Decl(typeofThis.ts, 45, 11)) } } module Test7 { ->Test7 : Symbol(Test7, Decl(typeofThis.ts, 46, 1)) +>Test7 : Symbol(Test7, Decl(typeofThis.ts, 47, 1)) export let f = () => { ->f : Symbol(f, Decl(typeofThis.ts, 49, 14)) +>f : Symbol(f, Decl(typeofThis.ts, 50, 14)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 50, 11)) +>x : Symbol(x, Decl(typeofThis.ts, 51, 11)) } } const Test8 = () => { ->Test8 : Symbol(Test8, Decl(typeofThis.ts, 54, 5)) +>Test8 : Symbol(Test8, Decl(typeofThis.ts, 55, 5)) let x: typeof this.no = 1; ->x : Symbol(x, Decl(typeofThis.ts, 55, 7)) +>x : Symbol(x, Decl(typeofThis.ts, 56, 7)) } class Test9 { ->Test9 : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) +>Test9 : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) no = 0; ->no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) this = 0; ->this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) f() { ->f : Symbol(Test9.f, Decl(typeofThis.ts, 60, 13)) +>f : Symbol(Test9.f, Decl(typeofThis.ts, 61, 13)) if (this instanceof Test9D1) { ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 83, 1)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 84, 1)) const d1: typeof this = this; ->d1 : Symbol(d1, Decl(typeofThis.ts, 64, 17)) +>d1 : Symbol(d1, Decl(typeofThis.ts, 65, 17)) d1.f1(); ->d1.f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) ->d1 : Symbol(d1, Decl(typeofThis.ts, 64, 17)) ->f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) +>d1.f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 86, 15)) +>d1 : Symbol(d1, Decl(typeofThis.ts, 65, 17)) +>f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 86, 15)) } if (this instanceof Test9D2) { ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 87, 1)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 88, 1)) const d2: typeof this = this; ->d2 : Symbol(d2, Decl(typeofThis.ts, 69, 17)) +>d2 : Symbol(d2, Decl(typeofThis.ts, 70, 17)) d2.f2(); ->d2.f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) ->d2 : Symbol(d2, Decl(typeofThis.ts, 69, 17)) ->f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) +>d2.f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 90, 15)) +>d2 : Symbol(d2, Decl(typeofThis.ts, 70, 17)) +>f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 90, 15)) } } g() { ->g : Symbol(Test9.g, Decl(typeofThis.ts, 72, 5)) +>g : Symbol(Test9.g, Decl(typeofThis.ts, 73, 5)) if (this.no === 1) { ->this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) const no: typeof this.no = this.no; ->no : Symbol(no, Decl(typeofThis.ts, 76, 17)) ->this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) ->no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) ->this.no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->no : Symbol(Test9.no, Decl(typeofThis.ts, 58, 13)) +>no : Symbol(no, Decl(typeofThis.ts, 77, 17)) +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) +>this.no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>no : Symbol(Test9.no, Decl(typeofThis.ts, 59, 13)) } if (this.this === 1) { ->this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) const no: typeof this.this = this.this; ->no : Symbol(no, Decl(typeofThis.ts, 80, 17)) ->this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) ->this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) ->this.this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) ->this : Symbol(Test9, Decl(typeofThis.ts, 56, 1)) ->this : Symbol(Test9.this, Decl(typeofThis.ts, 59, 11)) +>no : Symbol(no, Decl(typeofThis.ts, 81, 17)) +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) +>this.this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) +>this : Symbol(Test9, Decl(typeofThis.ts, 57, 1)) +>this : Symbol(Test9.this, Decl(typeofThis.ts, 60, 11)) } } } class Test9D1 { ->Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 83, 1)) +>Test9D1 : Symbol(Test9D1, Decl(typeofThis.ts, 84, 1)) f1() {} ->f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 85, 15)) +>f1 : Symbol(Test9D1.f1, Decl(typeofThis.ts, 86, 15)) } class Test9D2 { ->Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 87, 1)) +>Test9D2 : Symbol(Test9D2, Decl(typeofThis.ts, 88, 1)) f2() {} ->f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 89, 15)) +>f2 : Symbol(Test9D2.f2, Decl(typeofThis.ts, 90, 15)) +} + +class Test10 { +>Test10 : Symbol(Test10, Decl(typeofThis.ts, 92, 1)) + + a?: { b?: string } +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>b : Symbol(b, Decl(typeofThis.ts, 95, 9)) + + foo() { +>foo : Symbol(Test10.foo, Decl(typeofThis.ts, 95, 22)) + + let a: typeof this.a = undefined as any; +>a : Symbol(a, Decl(typeofThis.ts, 98, 11)) +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>undefined : Symbol(undefined) + + if (this.a) { +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>this : Symbol(Test10, Decl(typeofThis.ts, 92, 1)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) + + let a: typeof this.a = undefined as any; // should narrow to { b?: string } +>a : Symbol(a, Decl(typeofThis.ts, 100, 15)) +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>undefined : Symbol(undefined) + + let b: typeof this.a.b = undefined as any; +>b : Symbol(b, Decl(typeofThis.ts, 101, 15)) +>this.a.b : Symbol(b, Decl(typeofThis.ts, 95, 9)) +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>b : Symbol(b, Decl(typeofThis.ts, 95, 9)) +>undefined : Symbol(undefined) + + if (this.a.b) { +>this.a.b : Symbol(b, Decl(typeofThis.ts, 95, 9)) +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>this : Symbol(Test10, Decl(typeofThis.ts, 92, 1)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>b : Symbol(b, Decl(typeofThis.ts, 95, 9)) + + let b: typeof this.a.b = undefined as any; // should narrow to string +>b : Symbol(b, Decl(typeofThis.ts, 104, 19)) +>this.a.b : Symbol(b, Decl(typeofThis.ts, 95, 9)) +>this.a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>a : Symbol(Test10.a, Decl(typeofThis.ts, 94, 14)) +>b : Symbol(b, Decl(typeofThis.ts, 95, 9)) +>undefined : Symbol(undefined) + } + } + } +} + +class Test11 { +>Test11 : Symbol(Test11, Decl(typeofThis.ts, 108, 1)) + + this?: { x?: string }; +>this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>x : Symbol(x, Decl(typeofThis.ts, 111, 12)) + + foo() { +>foo : Symbol(Test11.foo, Decl(typeofThis.ts, 111, 26)) + + const o = this; +>o : Symbol(o, Decl(typeofThis.ts, 114, 13)) +>this : Symbol(Test11, Decl(typeofThis.ts, 108, 1)) + + let bar: typeof o.this = {}; +>bar : Symbol(bar, Decl(typeofThis.ts, 115, 11)) +>o.this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>o : Symbol(o, Decl(typeofThis.ts, 114, 13)) +>this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) + + if (o.this && o.this.x) { +>o.this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>o : Symbol(o, Decl(typeofThis.ts, 114, 13)) +>this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>o.this.x : Symbol(x, Decl(typeofThis.ts, 111, 12)) +>o.this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>o : Symbol(o, Decl(typeofThis.ts, 114, 13)) +>this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>x : Symbol(x, Decl(typeofThis.ts, 111, 12)) + + let y: string = o.this.x; // should narrow to string +>y : Symbol(y, Decl(typeofThis.ts, 118, 15)) +>o.this.x : Symbol(x, Decl(typeofThis.ts, 111, 12)) +>o.this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>o : Symbol(o, Decl(typeofThis.ts, 114, 13)) +>this : Symbol(Test11.this, Decl(typeofThis.ts, 110, 14)) +>x : Symbol(x, Decl(typeofThis.ts, 111, 12)) + } + } } diff --git a/tests/baselines/reference/typeofThis.types b/tests/baselines/reference/typeofThis.types index 612e415ec7e40..61d8c8080eea9 100644 --- a/tests/baselines/reference/typeofThis.types +++ b/tests/baselines/reference/typeofThis.types @@ -38,6 +38,15 @@ class Test1 { >data : { foo: string; } >{ foo: '' } : { foo: string; } >foo : string +>'' : "" + + var foo: typeof this.data.foo = ''; +>foo : string +>this.data.foo : string +>this.data : { foo: string; } +>this : any +>data : { foo: string; } +>foo : string >'' : "" var self: typeof this = this; @@ -86,13 +95,13 @@ function Test3(this: { no: number }) { function Test4(this: { no: number } | undefined) { >Test4 : (this: { no: number;} | undefined) => void ->this : { no: number; } +>this : { no: number; } | undefined >no : number let x: typeof this.no = 1; >x : number >this.no : number ->this : { no: number; } +>this : { no: number; } | undefined >no : number >1 : 1 } @@ -263,3 +272,108 @@ class Test9D2 { f2() {} >f2 : () => void } + +class Test10 { +>Test10 : Test10 + + a?: { b?: string } +>a : { b?: string | undefined; } | undefined +>b : string | undefined + + foo() { +>foo : () => void + + let a: typeof this.a = undefined as any; +>a : { b?: string | undefined; } | undefined +>this.a : { b?: string | undefined; } | undefined +>this : any +>a : { b?: string | undefined; } | undefined +>undefined as any : any +>undefined : undefined + + if (this.a) { +>this.a : { b?: string | undefined; } | undefined +>this : this +>a : { b?: string | undefined; } | undefined + + let a: typeof this.a = undefined as any; // should narrow to { b?: string } +>a : { b?: string | undefined; } +>this.a : { b?: string | undefined; } +>this : any +>a : { b?: string | undefined; } +>undefined as any : any +>undefined : undefined + + let b: typeof this.a.b = undefined as any; +>b : string | undefined +>this.a.b : string | undefined +>this.a : { b?: string | undefined; } +>this : any +>a : { b?: string | undefined; } +>b : string | undefined +>undefined as any : any +>undefined : undefined + + if (this.a.b) { +>this.a.b : string | undefined +>this.a : { b?: string | undefined; } +>this : this +>a : { b?: string | undefined; } +>b : string | undefined + + let b: typeof this.a.b = undefined as any; // should narrow to string +>b : string +>this.a.b : string +>this.a : { b?: string | undefined; } +>this : any +>a : { b?: string | undefined; } +>b : string +>undefined as any : any +>undefined : undefined + } + } + } +} + +class Test11 { +>Test11 : Test11 + + this?: { x?: string }; +>this : { x?: string | undefined; } | undefined +>x : string | undefined + + foo() { +>foo : () => void + + const o = this; +>o : this +>this : this + + let bar: typeof o.this = {}; +>bar : { x?: string | undefined; } | undefined +>o.this : { x?: string | undefined; } | undefined +>o : this +>this : { x?: string | undefined; } | undefined +>{} : {} + + if (o.this && o.this.x) { +>o.this && o.this.x : string | undefined +>o.this : { x?: string | undefined; } | undefined +>o : this +>this : { x?: string | undefined; } | undefined +>o.this.x : string | undefined +>o.this : { x?: string | undefined; } +>o : this +>this : { x?: string | undefined; } +>x : string | undefined + + let y: string = o.this.x; // should narrow to string +>y : string +>o.this.x : string +>o.this : { x?: string | undefined; } +>o : this +>this : { x?: string | undefined; } +>x : string + } + } +} diff --git a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts index dac4959646507..78eb779147ae2 100644 --- a/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts +++ b/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofThis.ts @@ -1,4 +1,5 @@ // @noImplicitThis: true +// @strict: true class Test { data = {}; @@ -12,6 +13,7 @@ class Test1 { ['this'] = ''; constructor() { var copy: typeof this.data = { foo: '' }; + var foo: typeof this.data.foo = ''; var self: typeof this = this; self.data; @@ -91,4 +93,33 @@ class Test9D1 { class Test9D2 { f2() {} +} + +class Test10 { + a?: { b?: string } + + foo() { + let a: typeof this.a = undefined as any; + if (this.a) { + let a: typeof this.a = undefined as any; // should narrow to { b?: string } + let b: typeof this.a.b = undefined as any; + + if (this.a.b) { + let b: typeof this.a.b = undefined as any; // should narrow to string + } + } + } +} + +class Test11 { + this?: { x?: string }; + + foo() { + const o = this; + let bar: typeof o.this = {}; + + if (o.this && o.this.x) { + let y: string = o.this.x; // should narrow to string + } + } } \ No newline at end of file From 1c63a5bed487fcd7f4676446bc189befcc802fcb Mon Sep 17 00:00:00 2001 From: Zzzen Date: Fri, 4 Jun 2021 21:27:07 +0800 Subject: [PATCH 5/5] update baselines --- tests/baselines/reference/typeofThis.types | 50 +++++++++++----------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/baselines/reference/typeofThis.types b/tests/baselines/reference/typeofThis.types index 61d8c8080eea9..a9dfbbfd2dc0e 100644 --- a/tests/baselines/reference/typeofThis.types +++ b/tests/baselines/reference/typeofThis.types @@ -277,56 +277,56 @@ class Test10 { >Test10 : Test10 a?: { b?: string } ->a : { b?: string | undefined; } | undefined +>a : { b?: string; } | undefined >b : string | undefined foo() { >foo : () => void let a: typeof this.a = undefined as any; ->a : { b?: string | undefined; } | undefined ->this.a : { b?: string | undefined; } | undefined +>a : { b?: string; } | undefined +>this.a : { b?: string; } | undefined >this : any ->a : { b?: string | undefined; } | undefined +>a : { b?: string; } | undefined >undefined as any : any >undefined : undefined if (this.a) { ->this.a : { b?: string | undefined; } | undefined +>this.a : { b?: string; } | undefined >this : this ->a : { b?: string | undefined; } | undefined +>a : { b?: string; } | undefined let a: typeof this.a = undefined as any; // should narrow to { b?: string } ->a : { b?: string | undefined; } ->this.a : { b?: string | undefined; } +>a : { b?: string; } +>this.a : { b?: string; } >this : any ->a : { b?: string | undefined; } +>a : { b?: string; } >undefined as any : any >undefined : undefined let b: typeof this.a.b = undefined as any; >b : string | undefined >this.a.b : string | undefined ->this.a : { b?: string | undefined; } +>this.a : { b?: string; } >this : any ->a : { b?: string | undefined; } +>a : { b?: string; } >b : string | undefined >undefined as any : any >undefined : undefined if (this.a.b) { >this.a.b : string | undefined ->this.a : { b?: string | undefined; } +>this.a : { b?: string; } >this : this ->a : { b?: string | undefined; } +>a : { b?: string; } >b : string | undefined let b: typeof this.a.b = undefined as any; // should narrow to string >b : string >this.a.b : string ->this.a : { b?: string | undefined; } +>this.a : { b?: string; } >this : any ->a : { b?: string | undefined; } +>a : { b?: string; } >b : string >undefined as any : any >undefined : undefined @@ -339,7 +339,7 @@ class Test11 { >Test11 : Test11 this?: { x?: string }; ->this : { x?: string | undefined; } | undefined +>this : { x?: string; } | undefined >x : string | undefined foo() { @@ -350,29 +350,29 @@ class Test11 { >this : this let bar: typeof o.this = {}; ->bar : { x?: string | undefined; } | undefined ->o.this : { x?: string | undefined; } | undefined +>bar : { x?: string; } | undefined +>o.this : { x?: string; } | undefined >o : this ->this : { x?: string | undefined; } | undefined +>this : { x?: string; } | undefined >{} : {} if (o.this && o.this.x) { >o.this && o.this.x : string | undefined ->o.this : { x?: string | undefined; } | undefined +>o.this : { x?: string; } | undefined >o : this ->this : { x?: string | undefined; } | undefined +>this : { x?: string; } | undefined >o.this.x : string | undefined ->o.this : { x?: string | undefined; } +>o.this : { x?: string; } >o : this ->this : { x?: string | undefined; } +>this : { x?: string; } >x : string | undefined let y: string = o.this.x; // should narrow to string >y : string >o.this.x : string ->o.this : { x?: string | undefined; } +>o.this : { x?: string; } >o : this ->this : { x?: string | undefined; } +>this : { x?: string; } >x : string } }