Skip to content

TypeScript - 联合类型

联合类型允许你定义一个变量或函数参数,它可以持有多种特定类型的值。它们提供了灵活性,同时仍然保持一定程度的类型安全,因为值必须是联合类型列表中指定的类型之一。

通过在两个或多个类型注解之间使用管道符号 (|) 来创建联合类型。

let variableName: Type1 | Type2 | Type3 | ...;
let identifier: string | number;
identifier = 12345; // 正确
console.log(`Numeric identifier: ${identifier}`);
identifier = "user-abc-123"; // 正确
console.log(`String identifier: ${identifier}`);
// identifier = true; // 错误:类型 'boolean' 不能赋值给类型 'string | number'。

在此示例中,identifier 可以存储 string 或 number 类型的值。

当你拥有一个联合类型的值时,TypeScript 只会允许你访问那些该联合类型中所有类型共有的成员(属性或方法),而不会首先执行类型检查。

function printValue(value: string | number) {
// console.log(value.toUpperCase()); // 错误:类型 'string | number' 上不存在属性 'toUpperCase'。类型 'number' 上不存在属性 'toUpperCase'。
// console.log(value.toFixed(2)); // 错误:类型 'string | number' 上不存在属性 'toFixed'。类型 'string' 上不存在属性 'string'。
// toString() 在 string 和 number 类型中都是共有的(通过原型链),所以这是允许的:
console.log(value.toString());
}

为了处理特定类型独有的成员,你需要在一个特定的代码块中将联合类型窄化 (narrow) 为一个更具体的类型。这可以通过使用类型守卫 (Type Guards) 来实现。

常见的类型守卫包括:

  • typeof 检查: 用于原始类型,如 string、number、boolean、symbol、bigint、undefined、function。
  • instanceof 检查: 用于检查对象是否是特定类的实例。
  • 属性存在性检查 (in 运算符): 检查对象是否包含某个特定属性。
  • 等值检查: 与特定的字面量值或 null/undefined 进行比较。
  • 自定义类型守卫函数: 返回类型谓词 (parameterName is Type) 的函数。

示例:带有类型守卫的联合类型和函数参数

Section titled “示例:带有类型守卫的联合类型和函数参数”
function processInput(input: string | string[] | null) {
if (typeof input === 'string') {
// 在这里,TypeScript 知道 'input' 是一个 string 类型
console.log(`Processing string: ${input.toUpperCase()}`);
}
// 使用 Array.isArray 进行数组检查(比 typeof 返回 'object' 更好)
else if (Array.isArray(input)) {
// 在这里,TypeScript 知道 'input' 是一个 string[] 类型
console.log(`Processing array: Found ${input.length} items.`);
input.forEach((item, index) => console.log(` Item ${index}: ${item}`));
}
// 显式检查是否为 null
else if (input === null) {
console.log("Input is null.");
}
// 注意:如果 null 是唯一剩余的选项,则这里不需要 else
}
processInput("hello");
console.log("---");
processInput(["Apple", "Banana", "Cherry"]);
console.log("---");
processInput(null);

联合类型可以应用于数组元素或对象属性。

要允许数组包含来自联合类型的不同类型的元素,可以使用括号:

// 可以存储 string 或 number 类型的数组
let mixedArray: (string | number)[] = [1, "two", 3, "four"];
mixedArray.push(5);
mixedArray.push("six");
// mixedArray.push(true); // 错误:类型 'boolean' 的参数不能赋值给类型 'string | number' 的参数。
console.log("Mixed Array:", mixedArray);
// 使用类型守卫处理混合数组
mixedArray.forEach(item => {
if (typeof item === 'string') {
console.log(`String item: ${item.toUpperCase()}`);
} else {
console.log(`Number item: ${item.toFixed(1)}`);
}
});

示例:用于数组类型的联合类型

Section titled “示例:用于数组类型的联合类型”

这定义了一个变量,它可以存储一个数字数组 (array of numbers) 或一个字符串数组 (array of strings),但不能在同一个数组中混合这两种类型。

let list: number[] | string[];
list = [1, 2, 3, 4]; // 正确 - 数字数组
console.log("Numeric Array:", list);
list = ["a", "b", "c"]; // 正确 - 字符串数组
console.log("String Array:", list);
// list = [1, "a"]; // 错误:类型 '(string | number)[]' 不能赋值给类型 'number[] | string[]'。
// 类型 '(string | number)[]' 不能赋值给类型 'string[]'。

联合类型是一个强大的特性,用于建模一个值可以合法地属于多种类型之一的场景,它在增强代码灵活性的同时,通过类型守卫指导开发者正确处理每一种可能性。