diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5980b113e7baf..84b33efb1e4b6 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2859,10 +2859,10 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { break; } // falls through - case SyntaxKind.ThisKeyword: - if (node.kind === SyntaxKind.ThisKeyword) { - seenThisKeyword = true; - } + case SyntaxKind.ThisKeyword: + if (node.kind === SyntaxKind.ThisKeyword || (node.kind === SyntaxKind.Identifier && (node as Identifier).escapedText === "this" && isPartOfTypeQuery(node))) { + seenThisKeyword = true; + } // TODO: Why use `isExpression` here? both Identifier and ThisKeyword are expressions. if (currentFlow && (isExpression(node) || parent.kind === SyntaxKind.ShorthandPropertyAssignment)) { (node as Identifier | ThisExpression).flowNode = currentFlow; diff --git a/tests/baselines/reference/typeofThisWithContextualThisParameter.js b/tests/baselines/reference/typeofThisWithContextualThisParameter.js new file mode 100644 index 0000000000000..16383fc6f009e --- /dev/null +++ b/tests/baselines/reference/typeofThisWithContextualThisParameter.js @@ -0,0 +1,113 @@ +//// [tests/cases/compiler/typeofThisWithContextualThisParameter.ts] //// + +//// [typeofThisWithContextualThisParameter.ts] +// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature + +interface A { a: string } + +// ---- Object-literal method case ---- + +{ + interface B { g: (this: A) => void } + + function f(_: B): void {} + + // Case 1: typeof this without explicit this usage — should resolve to A + f({ + g() { + type X = typeof this; + // ^ should be A + } + }) + + // Case 2: this used before typeof this — should resolve to A + f({ + g() { + this; + type X = typeof this; + // ^ should be A + } + }) + + // Case 3: this used after typeof this — should resolve to A + f({ + g() { + type X = typeof this; + // ^ should be A + this; + } + }) +} + +// ---- Function expression case ---- + +{ + function f(_: (this: A) => void): void {} + + // Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context) + f(function g() { + type X = typeof this; + }) + + // Case 5: this used before typeof this — should resolve to A + f(function g() { + this; + type X = typeof this; + }) + + // Case 6: this used after typeof this — should resolve to A + f(function g() { + type X = typeof this; + this; + }) +} + + +//// [typeofThisWithContextualThisParameter.js] +"use strict"; +// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature +// ---- Object-literal method case ---- +{ + function f(_) { } + // Case 1: typeof this without explicit this usage — should resolve to A + f({ + g() { + // ^ should be A + } + }); + // Case 2: this used before typeof this — should resolve to A + f({ + g() { + this; + // ^ should be A + } + }); + // Case 3: this used after typeof this — should resolve to A + f({ + g() { + // ^ should be A + this; + } + }); +} +// ---- Function expression case ---- +{ + function f(_) { } + // Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context) + f(function g() { + }); + // Case 5: this used before typeof this — should resolve to A + f(function g() { + this; + }); + // Case 6: this used after typeof this — should resolve to A + f(function g() { + this; + }); +} + + +//// [typeofThisWithContextualThisParameter.d.ts] +interface A { + a: string; +} diff --git a/tests/baselines/reference/typeofThisWithContextualThisParameter.symbols b/tests/baselines/reference/typeofThisWithContextualThisParameter.symbols new file mode 100644 index 0000000000000..c4e44df1fb44a --- /dev/null +++ b/tests/baselines/reference/typeofThisWithContextualThisParameter.symbols @@ -0,0 +1,123 @@ +//// [tests/cases/compiler/typeofThisWithContextualThisParameter.ts] //// + +=== typeofThisWithContextualThisParameter.ts === +// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature + +interface A { a: string } +>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0)) +>a : Symbol(A.a, Decl(typeofThisWithContextualThisParameter.ts, 2, 13)) + +// ---- Object-literal method case ---- + +{ + interface B { g: (this: A) => void } +>B : Symbol(B, Decl(typeofThisWithContextualThisParameter.ts, 6, 1)) +>g : Symbol(B.g, Decl(typeofThisWithContextualThisParameter.ts, 7, 17)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) +>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0)) + + function f(_: B): void {} +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40)) +>_ : Symbol(_, Decl(typeofThisWithContextualThisParameter.ts, 9, 15)) +>B : Symbol(B, Decl(typeofThisWithContextualThisParameter.ts, 6, 1)) + + // Case 1: typeof this without explicit this usage — should resolve to A + f({ +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40)) + + g() { +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 12, 7)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 13, 13)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) + + // ^ should be A + } + }) + + // Case 2: this used before typeof this — should resolve to A + f({ +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40)) + + g() { +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 20, 7)) + + this; +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 22, 17)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) + + // ^ should be A + } + }) + + // Case 3: this used after typeof this — should resolve to A + f({ +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 7, 40)) + + g() { +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 29, 7)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 30, 13)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) + + // ^ should be A + this; +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 7, 22)) + } + }) +} + +// ---- Function expression case ---- + +{ + function f(_: (this: A) => void): void {} +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1)) +>_ : Symbol(_, Decl(typeofThisWithContextualThisParameter.ts, 41, 15)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) +>A : Symbol(A, Decl(typeofThisWithContextualThisParameter.ts, 0, 0)) + + // Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context) + f(function g() { +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1)) +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 44, 6)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 44, 20)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) + + }) + + // Case 5: this used before typeof this — should resolve to A + f(function g() { +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1)) +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 49, 6)) + + this; +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 50, 13)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) + + }) + + // Case 6: this used after typeof this — should resolve to A + f(function g() { +>f : Symbol(f, Decl(typeofThisWithContextualThisParameter.ts, 40, 1)) +>g : Symbol(g, Decl(typeofThisWithContextualThisParameter.ts, 55, 6)) + + type X = typeof this; +>X : Symbol(X, Decl(typeofThisWithContextualThisParameter.ts, 55, 20)) +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) + + this; +>this : Symbol(this, Decl(typeofThisWithContextualThisParameter.ts, 41, 19)) + + }) +} + diff --git a/tests/baselines/reference/typeofThisWithContextualThisParameter.types b/tests/baselines/reference/typeofThisWithContextualThisParameter.types new file mode 100644 index 0000000000000..7d0ca011c06c0 --- /dev/null +++ b/tests/baselines/reference/typeofThisWithContextualThisParameter.types @@ -0,0 +1,178 @@ +//// [tests/cases/compiler/typeofThisWithContextualThisParameter.ts] //// + +=== typeofThisWithContextualThisParameter.ts === +// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature + +interface A { a: string } +>a : string +> : ^^^^^^ + +// ---- Object-literal method case ---- + +{ + interface B { g: (this: A) => void } +>g : (this: A) => void +> : ^ ^^ ^^^^^ +>this : A +> : ^ + + function f(_: B): void {} +>f : (_: B) => void +> : ^ ^^^^^^^^ +>_ : B +> : ^ + + // Case 1: typeof this without explicit this usage — should resolve to A + f({ +>f({ g() { type X = typeof this; // ^ should be A } }) : void +> : ^^^^ +>f : (_: B) => void +> : ^ ^^^^^^^^ +>{ g() { type X = typeof this; // ^ should be A } } : { g(this: A): void; } +> : ^^^^ ^^ ^^^^^^^^^^ + + g() { +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + // ^ should be A + } + }) + + // Case 2: this used before typeof this — should resolve to A + f({ +>f({ g() { this; type X = typeof this; // ^ should be A } }) : void +> : ^^^^ +>f : (_: B) => void +> : ^ ^^^^^^^^ +>{ g() { this; type X = typeof this; // ^ should be A } } : { g(this: A): void; } +> : ^^^^ ^^ ^^^^^^^^^^ + + g() { +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + this; +>this : A +> : ^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + // ^ should be A + } + }) + + // Case 3: this used after typeof this — should resolve to A + f({ +>f({ g() { type X = typeof this; // ^ should be A this; } }) : void +> : ^^^^ +>f : (_: B) => void +> : ^ ^^^^^^^^ +>{ g() { type X = typeof this; // ^ should be A this; } } : { g(this: A): void; } +> : ^^^^ ^^ ^^^^^^^^^^ + + g() { +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + // ^ should be A + this; +>this : A +> : ^ + } + }) +} + +// ---- Function expression case ---- + +{ + function f(_: (this: A) => void): void {} +>f : (_: (this: A) => void) => void +> : ^ ^^ ^^^^^ +>_ : (this: A) => void +> : ^ ^^ ^^^^^ +>this : A +> : ^ + + // Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context) + f(function g() { +>f(function g() { type X = typeof this; }) : void +> : ^^^^ +>f : (_: (this: A) => void) => void +> : ^ ^^ ^^^^^ +>function g() { type X = typeof this; } : (this: A) => void +> : ^ ^^ ^^^^^^^^^ +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + }) + + // Case 5: this used before typeof this — should resolve to A + f(function g() { +>f(function g() { this; type X = typeof this; }) : void +> : ^^^^ +>f : (_: (this: A) => void) => void +> : ^ ^^ ^^^^^ +>function g() { this; type X = typeof this; } : (this: A) => void +> : ^ ^^ ^^^^^^^^^ +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + this; +>this : A +> : ^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + }) + + // Case 6: this used after typeof this — should resolve to A + f(function g() { +>f(function g() { type X = typeof this; this; }) : void +> : ^^^^ +>f : (_: (this: A) => void) => void +> : ^ ^^ ^^^^^ +>function g() { type X = typeof this; this; } : (this: A) => void +> : ^ ^^ ^^^^^^^^^ +>g : (this: A) => void +> : ^ ^^ ^^^^^^^^^ + + type X = typeof this; +>X : A +> : ^ +>this : A +> : ^ + + this; +>this : A +> : ^ + + }) +} + diff --git a/tests/cases/compiler/typeofThisWithContextualThisParameter.ts b/tests/cases/compiler/typeofThisWithContextualThisParameter.ts new file mode 100644 index 0000000000000..a5f39e81aa0e3 --- /dev/null +++ b/tests/cases/compiler/typeofThisWithContextualThisParameter.ts @@ -0,0 +1,64 @@ +// @strict: true +// @target: es2015 +// @declaration: true + +// Reproduces #63616: typeof this inconsistent when this parameter comes from a contextual signature + +interface A { a: string } + +// ---- Object-literal method case ---- + +{ + interface B { g: (this: A) => void } + + function f(_: B): void {} + + // Case 1: typeof this without explicit this usage — should resolve to A + f({ + g() { + type X = typeof this; + // ^ should be A + } + }) + + // Case 2: this used before typeof this — should resolve to A + f({ + g() { + this; + type X = typeof this; + // ^ should be A + } + }) + + // Case 3: this used after typeof this — should resolve to A + f({ + g() { + type X = typeof this; + // ^ should be A + this; + } + }) +} + +// ---- Function expression case ---- + +{ + function f(_: (this: A) => void): void {} + + // Case 4: typeof this only — should resolve to A (not implicit any error for typeof this context) + f(function g() { + type X = typeof this; + }) + + // Case 5: this used before typeof this — should resolve to A + f(function g() { + this; + type X = typeof this; + }) + + // Case 6: this used after typeof this — should resolve to A + f(function g() { + type X = typeof this; + this; + }) +}