我有一个问题正在尝试解决,以确保任何扩展抽象类的对象都从作为泛型传入的接口定义属性。让我试着给你举个例子:
interface RelationshipData {
data: {
id: string;
type: string;
}
}
interface Dto<A = Record<string, any>, R = Record<string, RelationshipData>> {
attributes: A;
relationships: R;
}
abstract class EntityModel<T extends Dto> {
// How can I get T['attributes']'s properties and values to be defined on this class?
// Doing this just errors
[key in T['attributes']: T['attributes'][key];
}
以下是实际实施的方式:
interface ProductAttributes {
name: string;
description: string;
}
interface ProductRelationships {
tenant: RelationshipData;
}
interface ProductDto extends Dto<ProductAttributes, ProductRelationships> {}
export class ProductModel extends EntityModel<ProductDto> {
/**
* I want Typescript to error here that I haven't defined the following:
*
* name: string;
* description: string;
*/
}
我已经尝试了以上方法,但不起作用。
这在 TypeScript 中目前是不可能的。您无法以编程方式创建
abstract
属性(也就是说,您当前需要显式声明每个此类属性),并且类和接口都不允许拥有动态键。 microsoft/TypeScript#21326对此有一个长期开放的功能请求,但它不是该语言的一部分,您需要解决它。
ms/TS#21326 中提到的解决方法是将抽象类类型分为静态和动态部分,并添加单独的
implements
子句强制编译器检查动态部分:
abstract class EntityModel<T extends Dto> { }
type DynamicEntityModel<T extends Dto> = T["attributes"]
interface ProductDto extends Dto<ProductAttributes, ProductRelationships> {}
export class ProductModel // error!
// ~~~~~~~~~~~~
// Class 'ProductModel' incorrectly implements interface 'ProductAttributes'.
// Type 'EntityModel<ProductDto>' is missing the following properties
// from type 'ProductAttributes': name, description
extends EntityModel<ProductDto> implements DynamicEntityModel<ProductDto> {
}
另一种方法是将
EntityModel
编写为类工厂函数,需要调用者以某种方式传递来初始化模型的动态部分,它将为您生成非抽象类:
abstract class StaticEntityModel { }
type DynamicEntityModel<T extends Dto> = T["attributes"]
type EntityModel<T extends Dto> = StaticEntityModel & DynamicEntityModel<T>
function EntityModel<T extends Dto>(
addedBits: () => DynamicEntityModel<T>
) {
return class extends StaticEntityModel {
constructor() {
super();
Object.assign(this, addedBits())
}
} as new () => EntityModel<T>
}
function productDtoInitializer(): DynamicEntityModel<ProductDto> {
return {
description: "abc",
name: "xyz"
}
}
export class ProductModel extends EntityModel(productDtoInitializer) {
}
工厂函数需要类型断言,因为 TypeScript 无法验证初始化是否确实发生。