联合类型和交叉类型,是TypeScript类型系统里两个非常基础但又有点“反直觉”的概念。它们名字听起来像是对立的,但其实解决的是完全不同的问题。用好了,你的类型表达能力能上一个大台阶。
![图片[1]-如何处理联合类型和交叉类型?-速码派](http://www.sumapai.com/wp-content/uploads/2026/01/43986d1f1abb4a8b955742d93884ff0a_tplv-tb4s082cfz-aigc_resize_1080_1080-1024x683.webp)
先说联合类型,符号是竖线|。它的意思很简单:“这个值可以是A类型,或者B类型”。它描述的是多种可能性之一。
type ID = number | string;
let userId: ID = 100; // 可以
userId = 'abc123'; // 也可以
函数参数也经常这么用:
function formatInput(input: string | number) {
// ...
}
你看,input可能是字符串,也可能是数字。但这里就引出了处理联合类型时最关键的技巧:类型收窄。在函数体里,你不能直接调用input.toUpperCase(),因为数字没这个方法。你必须先判断当前到底是哪种可能。
function formatInput(input: string | number) {
if (typeof input === 'string') {
return input.toUpperCase(); // 这里input被收窄为string
} else {
return input.toFixed(2); // 这里input被收窄为number
}
}
用typeof、instanceof,或者检查特定的属性(“可辨识联合”),都能帮编译器确定当前的具体类型。这是处理联合类型的标准姿势。
交叉类型就完全是另一种思路了,符号是&。它的意思是:“这个值必须同时满足A类型和B类型的所有要求”。它描述的是类型的合并,创造出一个拥有所有特性的新类型。
interface Named {
name: string;
}
interface Aged {
age: number;
}
type Person = Named & Aged;
const p: Person = {
name: '张三',
age: 30 // 少一个都会报错
};
这有点像混入(mixin)。但交叉类型真正的威力(和坑)在于处理对象类型。它并不是简单地把两个对象拼起来,而是进行数学意义上的“交集”。对于属性名相同但类型不同的情况,结果类型会是never,因为没有任何值能同时满足两个冲突的类型。
那什么时候用哪个呢?我的经验法则挺简单:当你需要表达“或”的关系,比如一个函数能接受多种形态的参数,就用联合类型。当你需要表达“且”的关系,比如组合多个接口或特性来创建一个更丰富的类型,就用交叉类型。
实战中,它们经常一起出现。比如一个常见的模式:定义一个基础配置,再定义一个带默认值的配置,最后交叉起来。
interface BaseConfig {
url: string;
}
interface DefaultConfig {
timeout: number;
}
type FullConfig = BaseConfig & Partial<DefaultConfig>;
这里FullConfig必须有url,而timeout是可选的(因为被Partial包装了)。这就是交叉类型和工具类型的组合技。
理解它们的关键,是别再纠结于“联合”和“交叉”的字面意思。把它们看作描述类型集合的运算符:联合类型是取并集(值属于集合A或集合B),交叉类型是取交集(值必须同时属于集合A和集合B)。带着这个集合论的视角去看代码,很多奇怪的现象就说得通了。



























暂无评论内容