首页 > 解决方案 > 在 Typescript 中扩展多个接口或创建新类(多态)

问题描述

我开始在 typescript 中对 API 客户端进行建模,并寻找对数据建模的正确方法的一些说明。API 中的数据模型都共享一组通用属性(抽象),然后每个对象都有更具体的类型。还有一个每个对象将共享的公共元数据集 ($meta),但每个对象的某些值会有所不同。例如:

type Shapes = "Parallelogram" | "Triangle"
type QuadShapes = "Square" | "Trapezoid" | "Rectangle" | "Rhombus" | "Parallelogram"
type ParallelogramShapes = "Square" | "Rectangle" | "Rhombus" | "Parallelogram"
type TriangleShapes = "Isosceles" | "Scalene" | "Equilateral"


interface Meta {
    id: number
    number_of_sides: number,
    type: Shapes,
    is_quadrilateral: Boolean,
    is_parallelogram: Boolean,
}

interface IShape {
    $meta: Meta
}

interface IQuadrilateral extends IShape {
    $meta.number_of_sides: number
}

interface IParallelogram extends IQuadrilateral {
    $meta.number_of_parallel_sides: number
    $meta.type: ParallelogramShapes
}

interface ITriangle extends IShape {
    $meta.is_parallelogram: Boolean
    $meta.type: TriangleShapes
}

// ISquare should look like: 
//  {    
//      $meta: {
//          id: 1,
//          type: "Parallelogram",
//          parallelogram_type: "Square"
//          is_parallelogram: true,
//          is_quadrilateral: true,
//          number_of_sides: 4,
//          number_of_parallel_sides: 2
//      },
//      name: "Some Square"
//      color: "Blue"
//  }

interface ISquare extends IParallelogram {
    // How do i define the $meta node?
    $meta: 
    name: string,
    color: string
}

class Square implements ISquare {
    $meta: ?
    name: string,
    color: string

    constructor(meta: any, name: string, color: string) {
        this.$meta: meta
        this.name: name.
        this.color: color
    }
}

我不确定我是否遗漏了什么,但我遇到的最大问题是如何将$meta参数建模到接口中。如何以一致、非冗余的方式设置$meta.typeand 。$meta.is_parallelogram

标签: typescriptclassinterfacepolymorphism

解决方案


我倾向于重构,以便您的Meta类型形成自己的接口层次结构,并且在它支持的类型上IShape具有通用性。Meta

首先,我将重写您的类型。请注意,为了保持一致性,我认为您希望Shapes这样做,TriangleShapes | QuadShapes因为子类型化要求 ifSquare实现IShapetype属性可分配给IShapetype属性。But"Square"不能分配给"Parallelogram" | "Triangle",因为这些是字符串文字类型并且只引用特定的字符串:

type Shapes = TriangleShapes | QuadShapes
type QuadShapes = "Trapezoid" | ParallelogramShapes
type ParallelogramShapes = "Square" | "Rectangle" | "Rhombus" | "Parallelogram"
type TriangleShapes = "Isosceles" | "Scalene" | "Equilateral"

现在我们谈谈Meta层次结构。我认为,这涉及最少的冗余,因为您只需指定与其超类型的属性相比是新的或更窄的属性:

interface Meta {
    id: number
    number_of_sides: number,
    type: Shapes,
    is_quadrilateral: boolean, // Boolean is an object type, use boolean instead
    is_parallelogram: boolean, // ditto
}


interface QuadrilateralMeta extends Meta {
    number_of_sides: 4;
    type: QuadShapes;
    is_quadrilateral: true;
}

interface ParallelogramMeta extends QuadrilateralMeta {
    type: ParallelogramShapes;
    is_parallelogram: true;
}

interface TriangleMeta extends Meta {
    type: TriangleShapes;
    is_quadrilateral: false;
    is_parallelogram: false;
}

interface SquareMeta extends ParallelogramMeta {
    type: "Square"
    number_of_parallel_sides: 2; // not sure
}

现在我们介绍泛型IShape

interface IShape<M extends Meta> {
    $meta: M
}

并且ISquare可以通过指定泛型M并添加新属性来简明扼要地描述:

interface ISquare extends IShape<SquareMeta> {
    name: string;
    color: string;
}

最后是你的课:

class Square implements ISquare {
    constructor(public $meta: SquareMeta, public name: string, public color: string) { }
}

希望对您有用或给您一些想法。祝你好运!

链接到代码


推荐阅读