Skip to content

allow this in typeQuery #43898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13200,7 +13200,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) ? checkThisExpression(node.exprName) : checkExpression(node.exprName);
links.resolvedType = getRegularTypeOfLiteralType(getWidenedType(type));
}
return links.resolvedType;
}
Expand Down Expand Up @@ -21997,9 +21998,11 @@ 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 as Identifier) === getResolvedSymbol(target as Identifier) ||
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) &&
getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source as Identifier)) === getSymbolOfNode(target);
return isThisInTypeQuery(source) ?
target.kind === SyntaxKind.ThisKeyword :
target.kind === SyntaxKind.Identifier && getResolvedSymbol(source as Identifier) === getResolvedSymbol(target as Identifier) ||
(target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) &&
getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source as Identifier)) === getSymbolOfNode(target);
case SyntaxKind.ThisKeyword:
return target.kind === SyntaxKind.ThisKeyword;
case SyntaxKind.SuperKeyword:
Expand Down Expand Up @@ -24529,6 +24532,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);
Expand Down Expand Up @@ -24572,7 +24576,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);
}

Expand Down Expand Up @@ -27275,7 +27279,8 @@ namespace ts {
}

function checkQualifiedName(node: QualifiedName, checkMode: CheckMode | undefined) {
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, checkNonNullExpression(node.left), node.right, checkMode);
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);
}

function isMethodAccessForCall(node: Node) {
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4348,6 +4348,18 @@ namespace ts {
return !!node && node.kind === SyntaxKind.Identifier && identifierIsThisKeyword(node as Identifier);
}

export function isThisInTypeQuery(node: Node): boolean {
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 {
return id.originalKeywordKind === SyntaxKind.ThisKeyword;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>'.
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<T>'.
tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencingConstructorLocals.ts(19,9): error TS2304: Cannot find name 'z'.


Expand All @@ -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;
~
Expand All @@ -44,8 +44,8 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin
~
!!! error TS2339: Property 'z' does not exist on type 'D<T>'.
d: typeof this.z; // error
~~~~
!!! error TS2304: Cannot find name 'this'.
~
!!! error TS2339: Property 'z' does not exist on type 'D<T>'.
constructor(x: T) {
z = 1;
~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -31,9 +30,7 @@ tests/cases/conformance/classes/propertyMemberDeclarations/initializerReferencin

class E {
a = this.x; // ok
b: typeof this.x; // error
~~~~
!!! error TS2304: Cannot find name 'this'.
b: typeof this.x; // ok
constructor(public x) { }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) { }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ 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))

constructor(public x) { }
>x : Symbol(E.x, Decl(initializerReferencingConstructorParameters.ts, 17, 16))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
149 changes: 149 additions & 0 deletions tests/baselines/reference/typeofThis.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
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 (8 errors) ====
class Test {
data = {};
constructor() {
var copy: typeof this.data = {};
}
}

class Test1 {
data = { foo: '' };
['this'] = '';
constructor() {
var copy: typeof this.data = { foo: '' };
var foo: 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 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;
}

function Test4(this: { no: number } | undefined) {
let x: typeof this.no = 1;
~~~~
!!! error TS2532: Object is possibly 'undefined'.
}

class Test5 {
no = 1;

f = () => {
// should not capture this.
let x: typeof this.no = 1;
}
}

namespace Test6 {
export let f = () => {
let x: typeof this.no = 1;
~~~~
!!! 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.
}
}

module Test7 {
export let f = () => {
let x: typeof this.no = 1;
~~~~
!!! 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 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 {
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() {}
}

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
}
}
}
Loading