如何匹配 Typescript 中的类型?

kib*_*ibe 11 pattern-matching typescript

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

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

type UndeliveredOrderData =
  {
    OrderId: int;
  }

type Order = 
  | Delivered of DeliveredOrderData
  | Undelivered of UndeliveredOrderData
Run Code Online (Sandbox Code Playgroud)

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

let putOnTruck order = 
  match order with
    | Undelivered {OrderId=id} ->
      OutForDelivery {OrderId=id}
    | Delivered _ ->
      failwith "package already delivered"
Run Code Online (Sandbox Code Playgroud)

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

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

Lin*_*ste 6

这里起作用的两个主要打字稿概念是区分联合类型卫士

我们有两种不同类型的订单数据。区别在于,一个有DateDelivered,另一个没有。我们可以通过说UndeliveredOrderData可以never有 a DateDelivered(即该属性不得存在,或设置为undefined)来非常明确地表达这一点。

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

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

type Order = DeliveredOrderData | UndeliveredOrderData
Run Code Online (Sandbox Code Playgroud)

如果我们有一个Orderwhich可以是这两种类型中的任何一种,我们可以通过查看是否有a来判断它是哪种类型DateDelivered。我们将该逻辑放入用户定义的类型保护中,该保护告诉打字稿根据结果缩小类型范围。

const isDelivered = (order: Order): order is DeliveredOrderData => {
    return !! order.DateDelivered;
}
Run Code Online (Sandbox Code Playgroud)

假设我们有一个函数需要UndeliveredOrderData

const doPutOnTruck = (order: UndeliveredOrderData) => {
}
Run Code Online (Sandbox Code Playgroud)

但在运行时,我们不确定 anOrder是否被传递。我们可以在语句中使用类型保护if,并在两个分支中采取不同的行为。

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

Typescript Playground 链接


Ten*_*eff 4

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');
    }
}
Run Code Online (Sandbox Code Playgroud)