Finding a straightforward solution to this issue is challenging.
The name
attribute of class constructors is classified as a string
, consistent with the inherited properties from the Function interface. The compiler struggles with acknowledging the string
literal type associated with the name
. Refer to microsoft/TypeScript#43325 and its related concerns for more information.
Even if we can guarantee that during runtime, a class Foo {⋯}
will have Foo.name === "Foo"
(assuming we don't encounter peculiar scenarios where it gets overwritten by a static
property or subject to minification or obfuscation), TypeScript still faces challenges in assigning the type "Foo"
to Foo.name
. This complexity arises due to the hierarchical nature of the static side of a class, wherein, if Bar extends Foo {⋯}
exists, TypeScript anticipates Bar.name
to align with Foo.name
. Consequently, if Foo.name
is designated as "Foo"
, then Bar.name
must also correspond to "Foo"
. Realistically, this alignment rarely holds true during runtime. To navigate through these complications, TypeScript often avoids stringent types, leading to somewhat diluted specifics compared to users' preferences.
A hypothetical prospect involves the introduction of a nameof
operator in JavaScript akin to C#'s functionality, which could prompt TypeScript to extend support accordingly (refer to microsoft/TypeScript#1579). By waiting for such developments, one might anticipate an official method within TypeScript allowing extraction of class names within the type system.
Unless these anticipated changes materialize, users are reliant on manual specification. A viable approach entails consolidating relevant details manually, as illustrated below:
declare namespace MyClass {
export const name: "MyClass"
}
Subsequently, users may define their keys leveraging template literal types (providing the sole avenue for string concatenation at the type level):
const action1 = `${MyClass.name}_action1` as const;
const action2 = `${MyClass.name}_action2` as const;
interface Actions {
[action1]: string,
[action2]: number
}
This technique might partially address user requirements; however, until further advancements transpire, users face two options—implement similar strategies illustrated above or concede defeat.
Explore the code on Playground