diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1740df24a15c6..137565f2f157b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30498,10 +30498,37 @@ 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