如何在Typescript中匹配类型?

问题描述

在F#中,您可以执行以下操作:

type DeliveredOrderData =
  {
    OrderId: int;
    DateDelivered: DateTime;
  }

type UndeliveredOrderData =
  {
    OrderId: int;
  }

type Order = 
  | Delivered of DeliveredOrderData
  | Undelivered of UndeliveredOrderData

然后我可以创建根据状态返回的函数

let putOnTruck order = 
  match order with
    | Undelivered {OrderId=id} ->
      OutForDelivery {OrderId=id}
    | Delivered _ ->
      failwith "package already delivered"

我了解了如何在TypeScript中创建类型,但是我该如何与上面相同?

const putOrderOnTruck = (order: UndeliveredOrder) => {
  // how can I make sure order is really UndeliveredOrder?
}

解决方法

这里使用的两个主要打字稿概念是Discriminating UnionsType Guards

我们有两种不同类型的订单数据。区别在于,一个拥有DateDelivered,另一个没有。我们可以说UndeliveredOrderData可以never拥有DateDelivered(即,该属性不能存在或设置为undefined)来使之非常明确。

type DeliveredOrderData = {
    OrderId: number;
    DateDelivered: number;
  }

type UndeliveredOrderData = {
    OrderId: number;
    DateDelivered?: never;
}

type Order = DeliveredOrderData | UndeliveredOrderData

如果我们有一个Order可以是两种类型中的任何一种,我们可以通过查看是否存在DateDelivered来判断它是哪种类型。我们将该逻辑放入user-defined type guard中,该命令告诉Typescript根据结果缩小类型。

const isDelivered = (order: Order): order is DeliveredOrderData => {
    return !! order.DateDelivered;
}

假设我们有一个需要UndeliveredOrderData

的函数
const doPutOnTruck = (order: UndeliveredOrderData) => {
}

但是在运行时,我们不确定Order是否已交付。我们可以在if语句中使用类型保护,并在两个分支中采取不同的操作。

const maybePutOnTruck = (order: Order) => {
  if ( isDelivered( order ) ) {
      throw new Error("package already delivered");
  }
  // type of `order` is now `UndeliveredOrderData`
  doPutOnTruck(order);
}

Typescript Playground Link

,

TypeScript的类型只能用于静态类型检查,因此,为此,您应该检查对象的属性。

这是一个例子

enum OrderType {
    Delivered,Undelivered
}

type DeliveredOrderData = {
    type: OrderType
    OrderId: number;
    DateDelivered: Date
}

type UndeliveredOrderData = {
    type: OrderType
    OrderId: number;
}

function OutForDelivery(order: UndeliveredOrderData) {
}

let putOnTruck = (orderData: DeliveredOrderData | UndeliveredOrderData) => {
    switch(orderData.type) {
        case OrderType.Undelivered: 
            return OutForDelivery(orderData);
        case OrderType.Delivered:
            throw new Error('package already delivered');
    }
}