From 54e5ec9c9b799c6c610fb13a2068d46b99c0b95c Mon Sep 17 00:00:00 2001 From: puppy0cam <7194673+puppy0cam@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:29:19 +1000 Subject: [PATCH 1/2] fix: abstract properties are now properly elided from imports https://github.com/microsoft/TypeScript/issues/33525 --- src/compiler/checker.ts | 25 +++++++++++ .../abstractMethodComputedPropertyElision.js | 27 ++++++++++++ ...tractMethodComputedPropertyElision.symbols | 30 +++++++++++++ ...bstractMethodComputedPropertyElision.types | 42 +++++++++++++++++++ .../abstractMethodComputedPropertyElision.ts | 19 +++++++++ 5 files changed, 143 insertions(+) create mode 100644 tests/baselines/reference/abstractMethodComputedPropertyElision.js create mode 100644 tests/baselines/reference/abstractMethodComputedPropertyElision.symbols create mode 100644 tests/baselines/reference/abstractMethodComputedPropertyElision.types create mode 100644 tests/cases/compiler/abstractMethodComputedPropertyElision.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1740df24a15c6..94e8b95542009 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30498,10 +30498,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function markIdentifierAliasReferenced(location: Identifier) { const symbol = getResolvedSymbol(location); if (symbol && symbol !== argumentsSymbol && symbol !== unknownSymbol && !isThisInTypeQuery(location)) { + // Check if this identifier is in a computed property name of an abstract method/property + // If so, we should not mark it as referenced since abstract members are not emitted + if (isIdentifierInAbstractMemberComputedPropertyName(location)) { + return; + } markAliasReferenced(symbol, location); } } + function isIdentifierInAbstractMemberComputedPropertyName(node: Identifier): boolean { + // Walk up the AST to see if this identifier is within a computed property name + let parent = node.parent; + while (parent) { + if (isComputedPropertyName(parent)) { + // Found a computed property name, now check if it's for an abstract member + const memberParent = parent.parent; + if ((isMethodDeclaration(memberParent) || isMethodSignature(memberParent) || + isPropertyDeclaration(memberParent) || isPropertySignature(memberParent)) && + hasSyntacticModifier(memberParent, ModifierFlags.Abstract)) { + return true; + } + // If we found a computed property name but it's not for an abstract member, stop searching + return false; + } + parent = parent.parent; + } + return false; + } + function markPropertyAliasReferenced(location: PropertyAccessExpression | QualifiedName, propSymbol?: Symbol, parentType?: Type) { const left = isPropertyAccessExpression(location) ? location.expression : location.left; if (isThisIdentifier(left) || !isIdentifier(left)) { diff --git a/tests/baselines/reference/abstractMethodComputedPropertyElision.js b/tests/baselines/reference/abstractMethodComputedPropertyElision.js new file mode 100644 index 0000000000000..ce59075b796b1 --- /dev/null +++ b/tests/baselines/reference/abstractMethodComputedPropertyElision.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/abstractMethodComputedPropertyElision.ts] //// + +//// [sharedSymbol.ts] +export const sharedSymbol: unique symbol = Symbol(); + +//// [AbstractClassWithTypeImports.ts] +import type { sharedSymbol } from "./sharedSymbol.js"; + +export abstract class AbstractClassTypeImport { + public abstract [sharedSymbol]: string; +} + +//// [AbstractClassNormalImports.ts] +import { sharedSymbol } from "./sharedSymbol.js"; + +export abstract class AbstractClassNormalImport { + public abstract [sharedSymbol]: string; +} + +//// [sharedSymbol.js] +export const sharedSymbol = Symbol(); +//// [AbstractClassWithTypeImports.js] +export class AbstractClassTypeImport { +} +//// [AbstractClassNormalImports.js] +export class AbstractClassNormalImport { +} diff --git a/tests/baselines/reference/abstractMethodComputedPropertyElision.symbols b/tests/baselines/reference/abstractMethodComputedPropertyElision.symbols new file mode 100644 index 0000000000000..eb7c85a9eb06e --- /dev/null +++ b/tests/baselines/reference/abstractMethodComputedPropertyElision.symbols @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/abstractMethodComputedPropertyElision.ts] //// + +=== sharedSymbol.ts === +export const sharedSymbol: unique symbol = Symbol(); +>sharedSymbol : Symbol(sharedSymbol, Decl(sharedSymbol.ts, 0, 12)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --)) + +=== AbstractClassWithTypeImports.ts === +import type { sharedSymbol } from "./sharedSymbol.js"; +>sharedSymbol : Symbol(sharedSymbol, Decl(AbstractClassWithTypeImports.ts, 0, 13)) + +export abstract class AbstractClassTypeImport { +>AbstractClassTypeImport : Symbol(AbstractClassTypeImport, Decl(AbstractClassWithTypeImports.ts, 0, 54)) + + public abstract [sharedSymbol]: string; +>[sharedSymbol] : Symbol(AbstractClassTypeImport[sharedSymbol], Decl(AbstractClassWithTypeImports.ts, 2, 47)) +>sharedSymbol : Symbol(sharedSymbol, Decl(AbstractClassWithTypeImports.ts, 0, 13)) +} + +=== AbstractClassNormalImports.ts === +import { sharedSymbol } from "./sharedSymbol.js"; +>sharedSymbol : Symbol(sharedSymbol, Decl(AbstractClassNormalImports.ts, 0, 8)) + +export abstract class AbstractClassNormalImport { +>AbstractClassNormalImport : Symbol(AbstractClassNormalImport, Decl(AbstractClassNormalImports.ts, 0, 49)) + + public abstract [sharedSymbol]: string; +>[sharedSymbol] : Symbol(AbstractClassNormalImport[sharedSymbol], Decl(AbstractClassNormalImports.ts, 2, 49)) +>sharedSymbol : Symbol(sharedSymbol, Decl(AbstractClassNormalImports.ts, 0, 8)) +} diff --git a/tests/baselines/reference/abstractMethodComputedPropertyElision.types b/tests/baselines/reference/abstractMethodComputedPropertyElision.types new file mode 100644 index 0000000000000..bf49c214882f4 --- /dev/null +++ b/tests/baselines/reference/abstractMethodComputedPropertyElision.types @@ -0,0 +1,42 @@ +//// [tests/cases/compiler/abstractMethodComputedPropertyElision.ts] //// + +=== sharedSymbol.ts === +export const sharedSymbol: unique symbol = Symbol(); +>sharedSymbol : unique symbol +> : ^^^^^^^^^^^^^ +>Symbol() : unique symbol +> : ^^^^^^^^^^^^^ +>Symbol : SymbolConstructor +> : ^^^^^^^^^^^^^^^^^ + +=== AbstractClassWithTypeImports.ts === +import type { sharedSymbol } from "./sharedSymbol.js"; +>sharedSymbol : any +> : ^^^ + +export abstract class AbstractClassTypeImport { +>AbstractClassTypeImport : AbstractClassTypeImport +> : ^^^^^^^^^^^^^^^^^^^^^^^ + + public abstract [sharedSymbol]: string; +>[sharedSymbol] : string +> : ^^^^^^ +>sharedSymbol : unique symbol +> : ^^^^^^^^^^^^^ +} + +=== AbstractClassNormalImports.ts === +import { sharedSymbol } from "./sharedSymbol.js"; +>sharedSymbol : unique symbol +> : ^^^^^^^^^^^^^ + +export abstract class AbstractClassNormalImport { +>AbstractClassNormalImport : AbstractClassNormalImport +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ + + public abstract [sharedSymbol]: string; +>[sharedSymbol] : string +> : ^^^^^^ +>sharedSymbol : unique symbol +> : ^^^^^^^^^^^^^ +} diff --git a/tests/cases/compiler/abstractMethodComputedPropertyElision.ts b/tests/cases/compiler/abstractMethodComputedPropertyElision.ts new file mode 100644 index 0000000000000..669db634b6075 --- /dev/null +++ b/tests/cases/compiler/abstractMethodComputedPropertyElision.ts @@ -0,0 +1,19 @@ +// @module: esnext +// @target: esnext + +// @Filename: sharedSymbol.ts +export const sharedSymbol: unique symbol = Symbol(); + +// @Filename: AbstractClassWithTypeImports.ts +import type { sharedSymbol } from "./sharedSymbol.js"; + +export abstract class AbstractClassTypeImport { + public abstract [sharedSymbol]: string; +} + +// @Filename: AbstractClassNormalImports.ts +import { sharedSymbol } from "./sharedSymbol.js"; + +export abstract class AbstractClassNormalImport { + public abstract [sharedSymbol]: string; +} \ No newline at end of file From f276135539239d461e1fa20941cdedb5f6276516 Mon Sep 17 00:00:00 2001 From: puppy0cam <7194673+puppy0cam@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:54:08 +1000 Subject: [PATCH 2/2] use dprint formatter --- src/compiler/checker.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 94e8b95542009..137565f2f157b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30514,9 +30514,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (isComputedPropertyName(parent)) { // Found a computed property name, now check if it's for an abstract member const memberParent = parent.parent; - if ((isMethodDeclaration(memberParent) || isMethodSignature(memberParent) || - isPropertyDeclaration(memberParent) || isPropertySignature(memberParent)) && - hasSyntacticModifier(memberParent, ModifierFlags.Abstract)) { + if ( + (isMethodDeclaration(memberParent) || isMethodSignature(memberParent) || + isPropertyDeclaration(memberParent) || isPropertySignature(memberParent)) && + hasSyntacticModifier(memberParent, ModifierFlags.Abstract) + ) { return true; } // If we found a computed property name but it's not for an abstract member, stop searching