网站搜索

如何将鸭子类型的联合类型处理为 TypeScript 接口?


我刚接触 TypeScript,还在努力掌握它的用法。

我有一个形成时间线的事件数组。它们看起来像这样:

const timeline = [{
  type: 'payment',
  amount: '12.23 USD',
  when: '<iso timestamp>'
},{
  type: 'payment',
  amount: '12.23 USD',
  when: '<iso timestamp>'
},{
  type: 'refunded',
  amount: '2.00 USD',
  when: '<iso timestamp>'
},{
  type: 'payment',
  amount: '12.23 USD',
  when: '<iso timestamp>'
},{
  type: 'delinquent',
  when: '<iso timestamp>'
}]

所以我已经定义了一个 IEvent 作为联合类型:

interface IPaymentEvent {
  amount: string,
  when: string
}

interface IRefundedEvent {
  amount: string,
  when: string
}

interface IDelinquentEvent {
  when: string
}

type IEvent = IPaymentEvent | IRefundedEvent | IDelinquentEvent

问题是我不清楚如何在代码中使用这种类型信息。我如何将时间线转换为我刚刚定义的实际类型?当我遍历数组时,如何解构联合类型?

我尝试的样子是这样的:

class PaymentEvent implements IPaymentEvent {}
class RefundedEvent implements IRefundedEvent {}
class DelinquentEvent implements IDelinquentEvent {}

const duckTypeMap = {
  payment: PaymentEvent,
  refunded: RefundedEvent,
  delinquent: DelinquentEvent
}

const typedTimeline = timeline.map(x => {
  return duckTypeMap[x.type](x)
})

console.log(typedTimeline)

但是那并不太管用

我有一种感觉,这里一定有一个常见的做法。我也想知道是否有两种方法可以做到这一点,(1) 使用 ES6 类和 (2) 不使用 ES6 类。对于后者,如果我们告诉类型系统如何按鸭子类型处理 JSON,它似乎应该能提供帮助。

答案#1

你快完成了。还有几件事需要修正:

  1. 为了创建类型的实例,你应该使用 new 关键字 - new duckTypeMap[x.type];
  2. 为了初始化这个实例的字段,你应该创建一个复制构造函数,或者只是映射 JSON 对象(手动或使用某个库)。参考这个答案作为示例。
  3. 如果你的类实现了该接口,它应该声明该接口的成员。同时,不确定使用union类型能带来什么好处。为了拥有多态数组,你可以只定义一个包含when属性的单一接口IEvent,并在所有类中实现它。

像这样的东西:

interface IEvent {
    when: string
}

class PaymentEvent implements IEvent {
    public amount:string;
    public when:string;
}

const typedTimeline:IEvent[] = timeline.map(x => {
    let target = new duckTypeMap[x.type];
    for (const key in x) {
        target[key] = x[key];
    }
    return target;
});

在遍历“typed”数组时,要确定项目类型,你可以使用instanceof操作符:

if(item instanceof RefundedEvent)