diff --git a/tsc/internal/checker/nodebuilderimpl.go b/tsc/internal/checker/nodebuilderimpl.go index 91a3721ae0736..0f0a010195363 100644 --- a/tsc/internal/checker/nodebuilderimpl.go +++ b/tsc/internal/checker/nodebuilderimpl.go @@ -2843,7 +2843,8 @@ func getTypeAliasForTypeLiteral(c *Checker, t *Type) *ast.Symbol { } func (b *NodeBuilderImpl) shouldWriteTypeOfFunctionSymbol(symbol *ast.Symbol, typeId TypeId) (bool, *ast.Symbol) { - isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { + // `typeof C.name` can only be written when the member name is a valid identifier + isStaticMethodSymbol := symbol.Flags&ast.SymbolFlagsMethod != 0 && scanner.IsIdentifierText(symbol.Name, core.LanguageVariantStandard) && core.Some(symbol.Declarations, func(declaration *ast.Node) bool { return ast.IsStatic(declaration) && !b.ch.isLateBindableIndexSignature(ast.GetNameOfDeclaration(declaration)) }) isNonLocalFunctionSymbol := false diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.js b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.js new file mode 100644 index 0000000000000..8eddf980c03a8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.js @@ -0,0 +1,164 @@ +//// [tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts] //// + +//// [declarationEmitStaticMethodNonIdentifierNames.ts] +declare const uniqueSym: unique symbol; + +// none of these names can be written as `typeof Foo.` +export class Foo { + static #priv() { } + static getPriv() { + return Foo.#priv; + } + + static "quoted-name"() { } + static getQuoted() { + return Foo["quoted-name"]; + } + + static ["computed-name"]() { } + static getComputed() { + return Foo["computed-name"]; + } + + static 1() { } + static getNumeric() { + return Foo[1]; + } + + static [uniqueSym]() { } + static getUnique() { + return Foo[uniqueSym]; + } + + // property types go through the same path + static privRef = Foo.#priv; +} + +// identifier names still use `typeof` +export class Writable { + static normalName() { } + static getNormal() { + return Writable.normalName; + } + + static $dollar() { } + static getDollar() { + return Writable.$dollar; + } +} + +export class Generic { + static #priv(x: T): T { return x; } + static getPriv() { + return Generic.#priv; + } +} + +export class Recursive { + static #rec() { return Recursive.#rec; } + static getRec() { + return Recursive.#rec; + } +} + +// instance members already fall back to a structural type +export class Instance { + #priv() { } + getPriv() { + return this.#priv; + } +} + + +//// [declarationEmitStaticMethodNonIdentifierNames.js] +// none of these names can be written as `typeof Foo.` +export class Foo { + static #priv() { } + static getPriv() { + return Foo.#priv; + } + static "quoted-name"() { } + static getQuoted() { + return Foo["quoted-name"]; + } + static ["computed-name"]() { } + static getComputed() { + return Foo["computed-name"]; + } + static 1() { } + static getNumeric() { + return Foo[1]; + } + static [uniqueSym]() { } + static getUnique() { + return Foo[uniqueSym]; + } + // property types go through the same path + static privRef = Foo.#priv; +} +// identifier names still use `typeof` +export class Writable { + static normalName() { } + static getNormal() { + return Writable.normalName; + } + static $dollar() { } + static getDollar() { + return Writable.$dollar; + } +} +export class Generic { + static #priv(x) { return x; } + static getPriv() { + return Generic.#priv; + } +} +export class Recursive { + static #rec() { return Recursive.#rec; } + static getRec() { + return Recursive.#rec; + } +} +// instance members already fall back to a structural type +export class Instance { + #priv() { } + getPriv() { + return this.#priv; + } +} + + +//// [declarationEmitStaticMethodNonIdentifierNames.d.ts] +declare const uniqueSym: unique symbol; +export declare class Foo { + #private; + static getPriv(): () => void; + static "quoted-name"(): void; + static getQuoted(): () => void; + static ["computed-name"](): void; + static getComputed(): () => void; + static 1(): void; + static getNumeric(): () => void; + static [uniqueSym](): void; + static getUnique(): () => void; + static privRef: () => void; +} +export declare class Writable { + static normalName(): void; + static getNormal(): typeof Writable.normalName; + static $dollar(): void; + static getDollar(): typeof Writable.$dollar; +} +export declare class Generic { + #private; + static getPriv(): (x: T) => T; +} +export declare class Recursive { + #private; + static getRec(): () => /*elided*/ any; +} +export declare class Instance { + #private; + getPriv(): () => void; +} +export {}; diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.symbols b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.symbols new file mode 100644 index 0000000000000..337495062ab1b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.symbols @@ -0,0 +1,156 @@ +//// [tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts] //// + +=== declarationEmitStaticMethodNonIdentifierNames.ts === +declare const uniqueSym: unique symbol; +>uniqueSym : Symbol(uniqueSym, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 13)) + +// none of these names can be written as `typeof Foo.` +export class Foo { +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) + + static #priv() { } +>#priv : Symbol(Foo.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 3, 18)) + + static getPriv() { +>getPriv : Symbol(Foo.getPriv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 4, 22)) + + return Foo.#priv; +>Foo.#priv : Symbol(Foo.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 3, 18)) +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) + } + + static "quoted-name"() { } +>"quoted-name" : Symbol(Foo["quoted-name"], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 7, 5)) + + static getQuoted() { +>getQuoted : Symbol(Foo.getQuoted, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 9, 30)) + + return Foo["quoted-name"]; +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) +>"quoted-name" : Symbol(Foo["quoted-name"], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 7, 5)) + } + + static ["computed-name"]() { } +>["computed-name"] : Symbol(Foo["computed-name"], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 12, 5)) +>"computed-name" : Symbol(Foo["computed-name"], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 12, 5)) + + static getComputed() { +>getComputed : Symbol(Foo.getComputed, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 14, 34)) + + return Foo["computed-name"]; +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) +>"computed-name" : Symbol(Foo["computed-name"], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 12, 5)) + } + + static 1() { } +>1 : Symbol(Foo[1], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 17, 5)) + + static getNumeric() { +>getNumeric : Symbol(Foo.getNumeric, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 19, 18)) + + return Foo[1]; +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) +>1 : Symbol(Foo[1], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 17, 5)) + } + + static [uniqueSym]() { } +>[uniqueSym] : Symbol(Foo[uniqueSym], Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 22, 5)) +>uniqueSym : Symbol(uniqueSym, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 13)) + + static getUnique() { +>getUnique : Symbol(Foo.getUnique, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 24, 28)) + + return Foo[uniqueSym]; +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) +>uniqueSym : Symbol(uniqueSym, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 13)) + } + + // property types go through the same path + static privRef = Foo.#priv; +>privRef : Symbol(Foo.privRef, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 27, 5)) +>Foo.#priv : Symbol(Foo.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 3, 18)) +>Foo : Symbol(Foo, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 0, 39)) +} + +// identifier names still use `typeof` +export class Writable { +>Writable : Symbol(Writable, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 31, 1)) + + static normalName() { } +>normalName : Symbol(Writable.normalName, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 34, 23)) + + static getNormal() { +>getNormal : Symbol(Writable.getNormal, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 35, 27)) + + return Writable.normalName; +>Writable.normalName : Symbol(Writable.normalName, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 34, 23)) +>Writable : Symbol(Writable, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 31, 1)) +>normalName : Symbol(Writable.normalName, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 34, 23)) + } + + static $dollar() { } +>$dollar : Symbol(Writable.$dollar, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 38, 5)) + + static getDollar() { +>getDollar : Symbol(Writable.getDollar, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 40, 24)) + + return Writable.$dollar; +>Writable.$dollar : Symbol(Writable.$dollar, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 38, 5)) +>Writable : Symbol(Writable, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 31, 1)) +>$dollar : Symbol(Writable.$dollar, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 38, 5)) + } +} + +export class Generic { +>Generic : Symbol(Generic, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 44, 1)) + + static #priv(x: T): T { return x; } +>#priv : Symbol(Generic.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 46, 22)) +>T : Symbol(T, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 17)) +>x : Symbol(x, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 20)) +>T : Symbol(T, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 17)) +>T : Symbol(T, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 17)) +>x : Symbol(x, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 20)) + + static getPriv() { +>getPriv : Symbol(Generic.getPriv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 47, 42)) + + return Generic.#priv; +>Generic.#priv : Symbol(Generic.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 46, 22)) +>Generic : Symbol(Generic, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 44, 1)) + } +} + +export class Recursive { +>Recursive : Symbol(Recursive, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 51, 1)) + + static #rec() { return Recursive.#rec; } +>#rec : Symbol(Recursive.#rec, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 53, 24)) +>Recursive.#rec : Symbol(Recursive.#rec, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 53, 24)) +>Recursive : Symbol(Recursive, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 51, 1)) + + static getRec() { +>getRec : Symbol(Recursive.getRec, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 54, 44)) + + return Recursive.#rec; +>Recursive.#rec : Symbol(Recursive.#rec, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 53, 24)) +>Recursive : Symbol(Recursive, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 51, 1)) + } +} + +// instance members already fall back to a structural type +export class Instance { +>Instance : Symbol(Instance, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 58, 1)) + + #priv() { } +>#priv : Symbol(Instance.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 61, 23)) + + getPriv() { +>getPriv : Symbol(Instance.getPriv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 62, 15)) + + return this.#priv; +>this.#priv : Symbol(Instance.#priv, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 61, 23)) +>this : Symbol(Instance, Decl(declarationEmitStaticMethodNonIdentifierNames.ts, 58, 1)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.types b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.types new file mode 100644 index 0000000000000..6669693323db1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/declarationEmitStaticMethodNonIdentifierNames.types @@ -0,0 +1,157 @@ +//// [tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts] //// + +=== declarationEmitStaticMethodNonIdentifierNames.ts === +declare const uniqueSym: unique symbol; +>uniqueSym : unique symbol + +// none of these names can be written as `typeof Foo.` +export class Foo { +>Foo : Foo + + static #priv() { } +>#priv : () => void + + static getPriv() { +>getPriv : () => () => void + + return Foo.#priv; +>Foo.#priv : () => void +>Foo : typeof Foo + } + + static "quoted-name"() { } +>"quoted-name" : () => void + + static getQuoted() { +>getQuoted : () => () => void + + return Foo["quoted-name"]; +>Foo["quoted-name"] : () => void +>Foo : typeof Foo +>"quoted-name" : "quoted-name" + } + + static ["computed-name"]() { } +>["computed-name"] : () => void +>"computed-name" : "computed-name" + + static getComputed() { +>getComputed : () => () => void + + return Foo["computed-name"]; +>Foo["computed-name"] : () => void +>Foo : typeof Foo +>"computed-name" : "computed-name" + } + + static 1() { } +>1 : () => void + + static getNumeric() { +>getNumeric : () => () => void + + return Foo[1]; +>Foo[1] : () => void +>Foo : typeof Foo +>1 : 1 + } + + static [uniqueSym]() { } +>[uniqueSym] : () => void +>uniqueSym : unique symbol + + static getUnique() { +>getUnique : () => () => void + + return Foo[uniqueSym]; +>Foo[uniqueSym] : () => void +>Foo : typeof Foo +>uniqueSym : unique symbol + } + + // property types go through the same path + static privRef = Foo.#priv; +>privRef : () => void +>Foo.#priv : () => void +>Foo : typeof Foo +} + +// identifier names still use `typeof` +export class Writable { +>Writable : Writable + + static normalName() { } +>normalName : () => void + + static getNormal() { +>getNormal : () => () => void + + return Writable.normalName; +>Writable.normalName : () => void +>Writable : typeof Writable +>normalName : () => void + } + + static $dollar() { } +>$dollar : () => void + + static getDollar() { +>getDollar : () => () => void + + return Writable.$dollar; +>Writable.$dollar : () => void +>Writable : typeof Writable +>$dollar : () => void + } +} + +export class Generic { +>Generic : Generic + + static #priv(x: T): T { return x; } +>#priv : (x: T) => T +>x : T +>x : T + + static getPriv() { +>getPriv : () => (x: T) => T + + return Generic.#priv; +>Generic.#priv : (x: T) => T +>Generic : typeof Generic + } +} + +export class Recursive { +>Recursive : Recursive + + static #rec() { return Recursive.#rec; } +>#rec : () => any +>Recursive.#rec : () => any +>Recursive : typeof Recursive + + static getRec() { +>getRec : () => () => any + + return Recursive.#rec; +>Recursive.#rec : () => any +>Recursive : typeof Recursive + } +} + +// instance members already fall back to a structural type +export class Instance { +>Instance : Instance + + #priv() { } +>#priv : () => void + + getPriv() { +>getPriv : () => () => void + + return this.#priv; +>this.#priv : () => void +>this : this + } +} + diff --git a/tsc/testdata/tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts b/tsc/testdata/tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts new file mode 100644 index 0000000000000..a669b297a0541 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/declarationEmitStaticMethodNonIdentifierNames.ts @@ -0,0 +1,70 @@ +// @declaration: true +// @target: esnext + +declare const uniqueSym: unique symbol; + +// none of these names can be written as `typeof Foo.` +export class Foo { + static #priv() { } + static getPriv() { + return Foo.#priv; + } + + static "quoted-name"() { } + static getQuoted() { + return Foo["quoted-name"]; + } + + static ["computed-name"]() { } + static getComputed() { + return Foo["computed-name"]; + } + + static 1() { } + static getNumeric() { + return Foo[1]; + } + + static [uniqueSym]() { } + static getUnique() { + return Foo[uniqueSym]; + } + + // property types go through the same path + static privRef = Foo.#priv; +} + +// identifier names still use `typeof` +export class Writable { + static normalName() { } + static getNormal() { + return Writable.normalName; + } + + static $dollar() { } + static getDollar() { + return Writable.$dollar; + } +} + +export class Generic { + static #priv(x: T): T { return x; } + static getPriv() { + return Generic.#priv; + } +} + +export class Recursive { + static #rec() { return Recursive.#rec; } + static getRec() { + return Recursive.#rec; + } +} + +// instance members already fall back to a structural type +export class Instance { + #priv() { } + getPriv() { + return this.#priv; + } +}