在编译时获取泛型的名称
Obtain the name of a Generic on compile time
问题
有没有办法做到这一点:
type Example<T> = { [nameof T]: T }
^^^^^^
例如:
class MyClass {}
// So it would look like (In compile-time):
Example<MyClass> = { 'MyClass': MyClass }
动机
我之所以要这样做,是因为我正在使用 Sequelize,Sequelize 在做多对多关系时会自动创建对 Cross table 的引用。例如:
// Structure:
// User -- UserRole -- Role
// User object:
const user = User.find(...)
user.role[0].UserRole // <-- This object is based on the name of the Table
所以我的想法是有一个简单的 Mixin 类型,我可以用它来表示:
class User {
// Now role can now it has this property!
role: Array<CrossReferenceOf<Role, UserRole>>
}
TL;DR: 不,没有好的方法。
在您的示例中,名为 MyClass
的类型是提供给 MyClass
的 实例 的接口。由于您的示例代码没有定义属性或方法,因此它是 equivalent 到空对象类型 {}
。 MyClass
没有“名字” 属性 可言。所以没有办法编写 Example<MyClass>
并让编译器从中生成 {MyClass: MyClass}
。
您可能希望编译器可以查看名称 MyClass
并将其转换为 string literal "MyClass"
, but that isn't the way the TS type system works. TypeScript's type system is structural and not nominal。如果 TypeScript 中的两个类型具有相同的结构(相同类型的属性和方法),则它们是相同的类型。另一方面,仅仅因为 TypeScript 中的两种类型由不同的 names 引用或具有不同的 declarations,并不意味着它们不同类型:
interface AlsoMyClass { }
var x: MyClass;
var x: AlsoMyClass; // <-- no error on redeclaration
在这里您可以看到 x
变量被重新声明,没有错误。这表明编译器将 MyClass
和 AlsoMyClass
视为同一类型(尝试将其中一个更改为具有不同结构的类型,第二个声明将导致编译器错误)。
所以如果 Example<MyClass>
可以产生 {"MyClass": MyClass}
,那么 Example<AlsoMyClass>
将 也 必须产生 {"MyClass": MyClass}
和 不是{"AlsoMyClass": MyClass}
。至少在 tsc
编译器中,没有原则性的方法来处理 TypeScript 中赋予类型的名称。
人们可能还希望不要引用 MyClass
instance 类型,而是引用 MyClass
的类型]构造函数。毕竟,在运行时,MyClass
value 确实有一个 name
属性 should 相等至 "MyClass"
。所以也许你可以使用 Example<typeof MyClass>
,而不是 Example<MyClass>
。如果是这样,您可以这样定义 Example
:
type Example<T extends { name: string; new(...args: any): any }> =
Record<T['name'], InstanceType<T>>;
不幸的是,这也不是真的。编译器仅将 MyClass
构造函数的 name
属性 视为类型 string
:
const myClassName = MyClass.name; // string
所以 Example<typeof MyClass>
就是 {[k: string]: MyClass}
;不够好。
有一个悬而未决的问题,microsoft/TypeScript#32527 要求编译器将字符串文字 name
属性赋予具有已知名称的函数(例如 class 构造函数),但它没有看起来它会发生(特别是因为 name
属性 如果你缩小代码可能不是你所期望的)。
你可以尝试给MyClass
一个static name
属性,但是编译器也不喜欢那样,你需要使用 //@ts-ignore
:
抑制警告
class MyClass {
//@ts-ignore
declare static readonly name: "MyClass";
}
Example<typeof MyClass>
中的这个“有效”是 {MyClass: MyClass}
,但它很难看并且无法缩放。
所以不幸的是我会说“不,这里没什么好东西”。
问题
有没有办法做到这一点:
type Example<T> = { [nameof T]: T }
^^^^^^
例如:
class MyClass {}
// So it would look like (In compile-time):
Example<MyClass> = { 'MyClass': MyClass }
动机
我之所以要这样做,是因为我正在使用 Sequelize,Sequelize 在做多对多关系时会自动创建对 Cross table 的引用。例如:
// Structure:
// User -- UserRole -- Role
// User object:
const user = User.find(...)
user.role[0].UserRole // <-- This object is based on the name of the Table
所以我的想法是有一个简单的 Mixin 类型,我可以用它来表示:
class User {
// Now role can now it has this property!
role: Array<CrossReferenceOf<Role, UserRole>>
}
TL;DR: 不,没有好的方法。
在您的示例中,名为 MyClass
的类型是提供给 MyClass
的 实例 的接口。由于您的示例代码没有定义属性或方法,因此它是 equivalent 到空对象类型 {}
。 MyClass
没有“名字” 属性 可言。所以没有办法编写 Example<MyClass>
并让编译器从中生成 {MyClass: MyClass}
。
您可能希望编译器可以查看名称 MyClass
并将其转换为 string literal "MyClass"
, but that isn't the way the TS type system works. TypeScript's type system is structural and not nominal。如果 TypeScript 中的两个类型具有相同的结构(相同类型的属性和方法),则它们是相同的类型。另一方面,仅仅因为 TypeScript 中的两种类型由不同的 names 引用或具有不同的 declarations,并不意味着它们不同类型:
interface AlsoMyClass { }
var x: MyClass;
var x: AlsoMyClass; // <-- no error on redeclaration
在这里您可以看到 x
变量被重新声明,没有错误。这表明编译器将 MyClass
和 AlsoMyClass
视为同一类型(尝试将其中一个更改为具有不同结构的类型,第二个声明将导致编译器错误)。
所以如果 Example<MyClass>
可以产生 {"MyClass": MyClass}
,那么 Example<AlsoMyClass>
将 也 必须产生 {"MyClass": MyClass}
和 不是{"AlsoMyClass": MyClass}
。至少在 tsc
编译器中,没有原则性的方法来处理 TypeScript 中赋予类型的名称。
人们可能还希望不要引用 MyClass
instance 类型,而是引用 MyClass
的类型]构造函数。毕竟,在运行时,MyClass
value 确实有一个 name
属性 should 相等至 "MyClass"
。所以也许你可以使用 Example<typeof MyClass>
,而不是 Example<MyClass>
。如果是这样,您可以这样定义 Example
:
type Example<T extends { name: string; new(...args: any): any }> =
Record<T['name'], InstanceType<T>>;
不幸的是,这也不是真的。编译器仅将 MyClass
构造函数的 name
属性 视为类型 string
:
const myClassName = MyClass.name; // string
所以 Example<typeof MyClass>
就是 {[k: string]: MyClass}
;不够好。
有一个悬而未决的问题,microsoft/TypeScript#32527 要求编译器将字符串文字 name
属性赋予具有已知名称的函数(例如 class 构造函数),但它没有看起来它会发生(特别是因为 name
属性 如果你缩小代码可能不是你所期望的)。
你可以尝试给MyClass
一个static name
属性,但是编译器也不喜欢那样,你需要使用 //@ts-ignore
:
class MyClass {
//@ts-ignore
declare static readonly name: "MyClass";
}
Example<typeof MyClass>
中的这个“有效”是 {MyClass: MyClass}
,但它很难看并且无法缩放。
所以不幸的是我会说“不,这里没什么好东西”。