From a6dba88af6ecde52eae8ca89d6bd6c21849c0a6c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 26 May 2023 17:34:49 +0000 Subject: [PATCH] Cherry-pick PR #54208 into release-5.1 Component commits: d3529bdba0 Properly handle 'typeof this.xxx' in isTypeParameterPossiblyReferenced fda0a8f0e9 Add regression test e67193d0cb Merge branch 'main' into fix54167 --- src/compiler/checker.ts | 29 +++++++------------ .../typeofThisInMethodSignature.symbols | 23 +++++++++++++++ .../typeofThisInMethodSignature.types | 27 +++++++++++++++++ .../compiler/typeofThisInMethodSignature.ts | 11 +++++++ 4 files changed, 71 insertions(+), 19 deletions(-) create mode 100644 tests/baselines/reference/typeofThisInMethodSignature.symbols create mode 100644 tests/baselines/reference/typeofThisInMethodSignature.types create mode 100644 tests/cases/compiler/typeofThisInMethodSignature.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1f2d87de5da94..54fac52e166e1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18813,25 +18813,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.TypeQuery: const entityName = (node as TypeQueryNode).exprName; const firstIdentifier = getFirstIdentifier(entityName); - const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier); - const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called - let tpScope: Node; - if (tpDeclaration.kind === SyntaxKind.TypeParameter) { // Type parameter is a regular type parameter, e.g. foo - tpScope = tpDeclaration.parent; - } - else if (tp.isThisType) { - // Type parameter is the this type, and its declaration is the class declaration. - tpScope = tpDeclaration; - } - else { - // Type parameter's declaration was unrecognized. - // This could happen if the type parameter comes from e.g. a JSDoc annotation, so we default to returning true. - return true; - } - - if (firstIdentifierSymbol.declarations) { - return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) || - some((node as TypeQueryNode).typeArguments, containsReference); + if (!isThisIdentifier(firstIdentifier)) { // Don't attempt to analyze typeof this.xxx + const firstIdentifierSymbol = getResolvedSymbol(firstIdentifier); + const tpDeclaration = tp.symbol.declarations![0]; // There is exactly one declaration, otherwise `containsReference` is not called + const tpScope = tpDeclaration.kind === SyntaxKind.TypeParameter ? tpDeclaration.parent : // Type parameter is a regular type parameter, e.g. foo + tp.isThisType ? tpDeclaration : // Type parameter is the this type, and its declaration is the class declaration. + undefined; // Type parameter's declaration was unrecognized, e.g. comes from JSDoc annotation. + if (firstIdentifierSymbol.declarations && tpScope) { + return some(firstIdentifierSymbol.declarations, idDecl => isNodeDescendantOf(idDecl, tpScope)) || + some((node as TypeQueryNode).typeArguments, containsReference); + } } return true; case SyntaxKind.MethodDeclaration: diff --git a/tests/baselines/reference/typeofThisInMethodSignature.symbols b/tests/baselines/reference/typeofThisInMethodSignature.symbols new file mode 100644 index 0000000000000..0e0a71f4c90db --- /dev/null +++ b/tests/baselines/reference/typeofThisInMethodSignature.symbols @@ -0,0 +1,23 @@ +=== tests/cases/compiler/typeofThisInMethodSignature.ts === +// Repro from #54167 + +export class A { +>A : Symbol(A, Decl(typeofThisInMethodSignature.ts, 0, 0)) + + x = 1 +>x : Symbol(A.x, Decl(typeofThisInMethodSignature.ts, 2, 16)) + + a(x: typeof this.x): void {} +>a : Symbol(A.a, Decl(typeofThisInMethodSignature.ts, 3, 6)) +>x : Symbol(x, Decl(typeofThisInMethodSignature.ts, 4, 3)) +>this.x : Symbol(A.x, Decl(typeofThisInMethodSignature.ts, 2, 16)) +>this : Symbol(A, Decl(typeofThisInMethodSignature.ts, 0, 0)) +>x : Symbol(A.x, Decl(typeofThisInMethodSignature.ts, 2, 16)) +} + +const a = new A().a(1); +>a : Symbol(a, Decl(typeofThisInMethodSignature.ts, 7, 5)) +>new A().a : Symbol(A.a, Decl(typeofThisInMethodSignature.ts, 3, 6)) +>A : Symbol(A, Decl(typeofThisInMethodSignature.ts, 0, 0)) +>a : Symbol(A.a, Decl(typeofThisInMethodSignature.ts, 3, 6)) + diff --git a/tests/baselines/reference/typeofThisInMethodSignature.types b/tests/baselines/reference/typeofThisInMethodSignature.types new file mode 100644 index 0000000000000..00aa4d53eb28c --- /dev/null +++ b/tests/baselines/reference/typeofThisInMethodSignature.types @@ -0,0 +1,27 @@ +=== tests/cases/compiler/typeofThisInMethodSignature.ts === +// Repro from #54167 + +export class A { +>A : A + + x = 1 +>x : number +>1 : 1 + + a(x: typeof this.x): void {} +>a : (x: typeof this.x) => void +>x : number +>this.x : number +>this : this +>x : number +} + +const a = new A().a(1); +>a : void +>new A().a(1) : void +>new A().a : (x: number) => void +>new A() : A +>A : typeof A +>a : (x: number) => void +>1 : 1 + diff --git a/tests/cases/compiler/typeofThisInMethodSignature.ts b/tests/cases/compiler/typeofThisInMethodSignature.ts new file mode 100644 index 0000000000000..57821cadfd573 --- /dev/null +++ b/tests/cases/compiler/typeofThisInMethodSignature.ts @@ -0,0 +1,11 @@ +// @strict: true +// @noEmit: true + +// Repro from #54167 + +export class A { + x = 1 + a(x: typeof this.x): void {} +} + +const a = new A().a(1);