我有一个表示数据模型的抽象类。我们称之为模型。它实际上是在ModelData类型中参数化的,它表示模型实际具有的数据类型。所以:
export interface ModelData { type: string; attributes: {} }
export class Model<D extends ModelData> {}
export interface UserData extends ModelData
{ type: 'users', attributes: { name: string; }}
export class Users extends Model<UserData> {}
问题是我想保留模型构造函数的存储库。当我的数据存储向我发送一个形状如下的引用列表:{ type: 'users', id: 1 }
我希望能够refs.map(v => new ctors[v.type]({id: v.id}))
或类似的东西。
问题在于我不知道如何声明&#34;该对象包含一组字符串索引的子类构造函数。
我曾经做过
private types: { [type: string]: typeof Model }
然后
find<T extends ModelData>(ref: ModelReference): Model<T> {
return new types[ref.type]({id: ref.id})
}
或多或少(为简洁省略了额外的空值守卫和其他代码。
这实际上并不正确 - 我实际返回的内容是extends Model<T>
,而我在类型中存储的内容不是typeof Model
,而是{{ 1}}。
在2.4.1之前(我假设这与隐式通用更改有关)代码编译时没有抱怨,尽管不正确。我很高兴使正确,但我不确定如何使用打字稿来表达这一点。
我所获得的具体内容是一组extends typeof Model
类型,它们定义了ModelData的属性和关系属性的不同形状,以及一组为每个ModelData扩展Model的类。类型。因此,虽然从技术上讲我可以返回一个模型,但我宁愿返回一个用户,因为我可能会在User上添加一些额外的便利方法。
那么,是否可以说,&#34;这个东西包含一个字符串索引的构造函数组,所有这些构造函数都遵循模式&#34;扩展AbstractClass&#34;?
答案 0 :(得分:1)
我建议做类似的事情:
const types = {
'users': Users,
// ... other Models
}
function find<K extends keyof typeof types>(ref: { type: K }): typeof types[K]['prototype'] {
return new types[ref.type](); // this only works if all Models have no-arg constructors
}
如果您需要验证types
是否正确,可以使用new
关键字引用某个构造函数:
type Constructor<T> = {
new(...args: any[]): T;
readonly prototype: T;
}
如果我想说&#34;此对象的值是适当的Model
类型的构造函数,其中键与ModelData['type']
值相同&#34;,我&#39; d说:
type ModelMap<T> = {
[K in keyof T]: Constructor<Model<ModelData & { type: K }>>
}
function verifyTypes<T>(types: T, alsoTypes: ModelMap<T>) { }
verifyTypes(types, types);
如果verifyTypes()
给你编译错误,那是因为其中一个值不好:
const types = {
'user': Users
}
verifyTypes(types, types); // error: type of property 'user' is incompatible
请注意,如果您的Model
类型实际上保留了右ModelData
类型的某些属性,编译器就会生气。如果它们是空的,如您的示例所示,您的所有类型将在结构上相同,编译器将永远不会抱怨:
export class Model<D extends ModelData> {
data: D; // that's enough
}
希望有所帮助。祝你好运!