Skip to content
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

allow this in typeQuery #43898

Merged
merged 7 commits into from
Jun 17, 2021
Merged
Show file tree
Hide file tree
Changes from 4 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 @@ -13183,7 +13183,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 @@ -24513,6 +24516,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 @@ -24556,7 +24560,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 @@ -27253,7 +27257,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(checkThisExpression(node.left), node.left) : checkNonNullExpression(node.left);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this work when the QualifiedName is nested, like this.x.y? I think you need a test for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't work for this.x.y. I've replaced isTypeQueryNode with isPartOfTypeQuery

return checkPropertyAccessExpressionOrQualifiedName(node, node.left, leftType, node.right, checkMode);
}

function isMethodAccessForCall(node: Node) {
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4348,6 +4348,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));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the QualifiedName case doesn't check that its ultimate parent is a TypeQuery, so I think you could maybe fool this with this.x in a weird location, or o.this.x The latter should be easy to add a test for; I don't know what test you need for the former.

}

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
113 changes: 113 additions & 0 deletions tests/baselines/reference/typeofThis.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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) ====
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 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;
}

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'.
}

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() {}
}
Loading