需要 Typescript 抽象类来定义应在父类上定义的另一个接口的属性

问题描述 投票:0回答:1

我有一个问题正在尝试解决,以确保任何扩展抽象类的对象都从作为泛型传入的接口定义属性。让我试着给你举个例子:

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 typescript-generics
1个回答
0
投票

这在 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 无法验证初始化是否确实发生。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.