最初发表于https://antondevtips.com。
什么是歧视联合通常,应用程序需要能够处理多种输入或输出类型的变量和函数。对于这种需求,TypeScript 提供了一种称为“区分联合”的特殊联合类型,这种特殊联合类型也称为区分联合。
标记联合类型在TypeScript中,由两种或多种其他类型组成。一个标记联合类型的变量可以是联合中定义的任何一种类型。
下面是一个简单的例子,变量可以存储一个数字或字符串:
let id: number | string;
id = 100; // 这段代码没问题
id = "200"; // 这段代码没问题
// TypeScript 报错:类型 'boolean' 不能赋值给类型 'string | number'。
id = false; // 这里会报错
联合在拼接字符串字面量时也非常有用。字面量在TypeScript中是一种特殊的类型,常用于表示常量字符串。例如:
let text: "TypeScript" = "TypeScript";
text = "TypeScript"; // 这段代码可以编译成功
// TypeScript 错误:类型 '"JavaScript"' 不能赋值给类型 '"TypeScript"'。
text = "JavaScript";
通过将具体的值组合在一起,你可以表明一个变量只能取一些特定的值。
function print(value: string, align: "left" | "right" | "center") {
// ...
}
print("TypeScript types", "left")
// TypeScript 错误:"align" 参数应该是以下之一:"左" | "右" | "中"
print("TypeScript types", "middle");
当使用差异化联合体时,TypeScript只会让你访问这些类型共有的属性或方法。如果你想访问特定类型的属性或方法,你需要使用类型守卫来确定具体类型。
function 获取长度(obj: 字符串 | 字符串数组) {
if (typeof obj === "字符串") {
return obj.length; // 返回字符串长度
}
return obj.length; // 返回字符串数组的元素数量
}
带有对象类型的联合类型
联合类型也可以结合对象类型。下面让我们通过一些几何图形的例子来探索,这些图形既有共同点又有不同之处:
interface 圆形 = {
type: "circle";
半径: number;
};
interface 正方形 = {
type: "square";
边长: number;
};
让我们为每个形状创建一个计算面积的函数。
function getSquare(shape: Circle | Square) {
if (shape.type === "circle") {
return Math.PI * shape.radius * shape.radius;
}
if (shape.type === "square") {
return shape.size * shape.size;
}
}
const circle: Circle = {
type: "circle",
radius: 10
};
const square: Square = {
type: "square",
size: 5
};
console.log("圆的面积: ", getSquare(circle));
console.log("正方形的面积: ", getSquare(square));
这里,getSquare
函数接收一个形状参数,这个参数可以是两种类型之一:Circle
或 Square
。处理自定义类型时,你可以利用一个属性来区分不同的类型,比如我们的例子中使用的是 type
属性。这里的类型守卫非常简单直接:
// 计算形状的面积
function getSquare(shape: Circle | Square) {
// 如果形状是圆形,计算圆的面积
if (shape.type === "circle") {
return Math.PI * shape.radius * shape.radius;
}
// 如果形状是正方形,计算正方形的面积
if (shape.type === "square") {
return shape.side * shape.side;
}
return 0;
}
TypeScript 能够识别我们正在处理的圆形或正方形类型。
使用区分联合作为函数返回值另一个有用的场景是函数返回一个联合类型。让我们通过一个通过产品ID和所需数量订购产品的例子来探讨这种函数调用。这个函数的返回类型将是一个表示三种可能状态的联合类型:它可以表示成功、失败或未找到状态。
- 订单创建成功啦
- 找不到该产品
- 仓库里的商品不够
简单的方法是声明一个包含所有三种情况的大对象,但这样做会比较麻烦。
歧视性联盟提供了一种更简洁的解决方案:
type OrderResponse = OrderCreated | ProductNotFound | NoProductQuantity;
// 订单响应类型可以是订单创建成功、未找到产品或产品数量不足
type OrderCreated = {
类型: "order-created";
orderId: string;
message: string;
};
// 订单创建成功的类型定义,包含订单ID和消息
type ProductNotFound = {
类型: "product-not-found";
message: string;
};
// 未找到产品的类型定义,包含消息
type NoProductQuantity = {
类型: "no-product-quantity";
message: string;
};
// 产品数量不足的类型定义,包含消息
现在我们可以用这种联合体类型作为函数的返回类型。为了简化,我们假设从数据库或外部 API 获取产品数据,但实际上我们会使用模拟数据。
function orderProduct(productId: string, quantity: number): OrderResponse {
const productsDatabase = {
"Mobile Phone": { stock: 10 },
"Smart TV": { stock: 0 },
};
const product = productsDatabase[productId];
if (!product) {
return {
类型: "product-not-found",
消息: "找不到该商品"
};
}
if (quantity > product.stock) {
return {
类型: "no-product-quantity",
消息: "库存商品不足"
};
}
return {
类型: "order-created",
订单ID: `order-${new Date().getTime()}`,
消息: "订单已成功建立"
};
}
在这个函数中,我们将返回三种类型的响应:“订单创建成功”,“未找到产品”,“无产品库存”。要订购产品,我们可以调用这个方法。
const orderResult = orderProduct("Mobile Phone", 1);
switch (orderResult.type) {
case "order-created":
console.log(`成功了:${orderResult.message} (订单号:${orderResult.orderId})`);
break;
case "product-not-found":
console.log(`出错了:${orderResult.message}`);
break;
case "no-product-quantity":
console.log(`出错了:${orderResult.message}`);
break;
}
正如你所见,区分联合体在这种情况下使代码更易读和更易维护。
总结联合体类型在TypeScript中提供了一种有效的管理不同类型数据的方式。它们增强了类型的安全性,使代码更清晰,并确保所有可能的情况都被妥善处理,从而减少运行时错误。将联合体类型用作函数输入和输出类型将显著提高您的TypeScript应用程序的质量和可维护性。
希望这篇博客对你有用,编程开心!
最早发布于https://antondevtips.com。
在看完帖子之后,请思考一下这些:如果你喜欢我的内容... 可以支持我一下
加入我的Patreon和“请我喝杯咖啡”社区,解锁我的博客文章中的源代码独家访问!