问题描述
在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 Unions和Type 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的类型只能用于静态类型检查,因此,为此,您应该检查对象的属性。
这是一个例子
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');
}
}