联合类型、交叉类型与类型别名到这一篇TypeScript 才真正开始像一门“类型语言”。前面的基础类型、对象、函数、接口更多是在建立类型意识而联合类型与交叉类型则开始让你具备表达复杂业务关系的能力。很多人学习 TypeScript 时真正的分水岭就出现在这里。因为从这一刻起你不再只是在回答“一个值是什么类型”而是在回答更复杂的问题一个值可能处于哪些合法形态一个结构需要同时具备哪些特征一个类型表达式是否值得被命名和复用这也是 TypeScript 从“带标注的 JavaScript”进入“有建模能力的类型系统”的关键一步。联合类型表达“几种可能之一”最基础的联合类型写法如下letvalue:string|number;valuehello;value123;这表示value的合法范围不是单一类型而是多种类型中的一种。联合类型非常适合表达“不确定但有限”的输入和状态。最常见的例子是状态建模typeLoadingStateidle|loading|success|error;这行代码看起来很简单但它已经比string强很多因为它把状态空间从“任意字符串”缩小成了“真实业务允许的四种值”。联合类型在实际项目里为什么这么重要因为现实业务中大量值并不是单一形态而是“多种合法结果之一”。例如一个函数参数可能接收string或number一个接口响应可能成功也可能失败一个 UI 状态可能是loading、empty、success、error一个组件 prop 可能允许不同配置模式如果不用联合类型你只能要么写成特别宽泛的类型要么靠注释和约定表达这些分支这两种方式都不如显式建模稳定。联合类型的限制恰恰是它的价值看下面这个例子functionprintId(id:string|number){console.log(id.toUpperCase());}这段代码会报错。原因不是 TypeScript 太刻板而是它在坚持一个很合理的原则你只能直接使用所有候选类型都共同拥有的能力。因为在string | number里toUpperCase()只属于string不属于number。所以在你没有进一步判断之前编译器拒绝你这么做。这也是为什么联合类型经常会和“类型缩小”绑定出现。联合类型负责表达可能性类型缩小负责在代码分支里确认当前到底是哪一种。交叉类型表达“同时满足多个约束”交叉类型用表示typeBaseUser{id:number;name:string;};typeWithTimestamps{createdAt:string;updatedAt:string;};typeUserBaseUserWithTimestamps;这里的User表示它既要满足BaseUser也要满足WithTimestamps。和联合类型不同交叉类型不是几选一而是约束叠加。这在真实项目里很常见例如基础实体 审计字段领域对象 权限信息业务数据 分页元信息组件 props 通用样式属性交叉类型很适合做“特征组合”你可以把它理解成积木拼接但这个比喻有一个前提拼接的几块积木之间不能彼此冲突。例如typeWithId{id:number;};typeWithName{name:string;};typeUserWithIdWithName;这里的组合就很自然因为两个结构互补。但交叉类型不是万能拼接器很多初学者第一次接触时会以为它就是“把两个对象简单合并”。大多数互补场景下确实接近这样但如果字段冲突结果往往没有你想得那么直观。typeA{value:string};typeB{value:number};typeCAB;这里的C[value]不会 magically 变成“字符串或数字”而会进入一种几乎不可满足的状态因为一个值不可能同时既是string又是number。所以一个非常实用的工程判断是交叉类型适合合并互补约束不适合硬拼彼此冲突的模型。type的价值不只是给对象命名很多人最开始把type只当成“另一种写对象的方式”其实它更大的价值在于可以给任意类型表达式起别名。typeIDstring|number;typePoint[number,number];typeHandler(message:string)void;typeRequestStatusidle|loading|success|error;这件事很重要因为一旦类型表达式开始变复杂命名本身就是在提升可读性。对比下面两种写法functionfindUser(id:string|number){}typeUserIdstring|number;functionfindUser(id:UserId){}第二种更易读也更利于复用。因为“联合类型本身”只是语法“命名后的类型别名”才更接近系统里的业务概念。一个典型场景用联合类型表达接口结果假设一个登录接口要么成功要么失败typeLoginSuccess{success:true;token:string;userId:number;};typeLoginFailure{success:false;message:string;};typeLoginResultLoginSuccess|LoginFailure;这就是联合类型最有力量的地方。你不必用一堆可选字段去糊一个宽对象而是明确告诉系统返回值只有两种合法形态。相比下面这种写法typeLoginResult{success:boolean;token?:string;userId?:number;message?:string;};前者的表达能力强得多因为它保留了状态和字段之间的真实关系。一个典型场景用交叉类型叠加通用字段typeEntity{id:number;};typeTimestamps{createdAt:string;updatedAt:string;};typeArticleEntityTimestamps{title:string;content:string;};这类组合在工程里非常常见。它不是为了炫技而是为了减少重复并让公共结构保持统一。如何判断该用联合还是交叉这是一个非常值得反复练习的判断如果你表达的是“几种合法形态之一”用联合类型如果你表达的是“同时具备多个特征”用交叉类型如果一个类型表达式会反复出现给它起一个别名你也可以把它翻译成更口语的版本“或者”通常对应联合“并且”通常对应交叉初学者常见误区误区一联合类型写出来后直接按某一类使用这会立刻遇到报错。不是 TypeScript 在找麻烦而是你还没有告诉编译器当前到底是哪一种情况。误区二交叉类型被当成“对象合并万金油”如果两个模型本身矛盾交叉后的结果只会更难用不会更强大。误区三复杂类型不命名一长串联合、交叉、函数签名如果没有名字代码会很快失去可读性。类型别名很多时候不是可选项而是沟通工具。本文小结联合类型和交叉类型是 TypeScript 表达复杂业务关系的基础工具。联合类型让你描述“这个值可能是哪几种合法形态之一”交叉类型让你描述“这个值必须同时满足哪些约束”。而type作为类型别名则让这些表达可以被命名、复用和沟通。从这一篇开始你应该逐渐放弃“变量是什么类型”的单点思维转向“一个业务对象可能处于哪些合法状态”的系统思维。这才是 TypeScript 真正开始有建模力量的地方。练习定义一个Result类型表示ok或error再扩展成一个真正可用的成功/失败对象联合。定义UserBase和UserProfile然后用交叉类型合成UserDetail。思考登录接口返回成功和失败两种结构时为什么联合类型通常比“一个大对象里全是可选字段”更合适。后记2026年5月21日于上海。
【Typescript】05-联合类型交叉类型与类型别名
发布时间:2026/5/21 23:25:29
联合类型、交叉类型与类型别名到这一篇TypeScript 才真正开始像一门“类型语言”。前面的基础类型、对象、函数、接口更多是在建立类型意识而联合类型与交叉类型则开始让你具备表达复杂业务关系的能力。很多人学习 TypeScript 时真正的分水岭就出现在这里。因为从这一刻起你不再只是在回答“一个值是什么类型”而是在回答更复杂的问题一个值可能处于哪些合法形态一个结构需要同时具备哪些特征一个类型表达式是否值得被命名和复用这也是 TypeScript 从“带标注的 JavaScript”进入“有建模能力的类型系统”的关键一步。联合类型表达“几种可能之一”最基础的联合类型写法如下letvalue:string|number;valuehello;value123;这表示value的合法范围不是单一类型而是多种类型中的一种。联合类型非常适合表达“不确定但有限”的输入和状态。最常见的例子是状态建模typeLoadingStateidle|loading|success|error;这行代码看起来很简单但它已经比string强很多因为它把状态空间从“任意字符串”缩小成了“真实业务允许的四种值”。联合类型在实际项目里为什么这么重要因为现实业务中大量值并不是单一形态而是“多种合法结果之一”。例如一个函数参数可能接收string或number一个接口响应可能成功也可能失败一个 UI 状态可能是loading、empty、success、error一个组件 prop 可能允许不同配置模式如果不用联合类型你只能要么写成特别宽泛的类型要么靠注释和约定表达这些分支这两种方式都不如显式建模稳定。联合类型的限制恰恰是它的价值看下面这个例子functionprintId(id:string|number){console.log(id.toUpperCase());}这段代码会报错。原因不是 TypeScript 太刻板而是它在坚持一个很合理的原则你只能直接使用所有候选类型都共同拥有的能力。因为在string | number里toUpperCase()只属于string不属于number。所以在你没有进一步判断之前编译器拒绝你这么做。这也是为什么联合类型经常会和“类型缩小”绑定出现。联合类型负责表达可能性类型缩小负责在代码分支里确认当前到底是哪一种。交叉类型表达“同时满足多个约束”交叉类型用表示typeBaseUser{id:number;name:string;};typeWithTimestamps{createdAt:string;updatedAt:string;};typeUserBaseUserWithTimestamps;这里的User表示它既要满足BaseUser也要满足WithTimestamps。和联合类型不同交叉类型不是几选一而是约束叠加。这在真实项目里很常见例如基础实体 审计字段领域对象 权限信息业务数据 分页元信息组件 props 通用样式属性交叉类型很适合做“特征组合”你可以把它理解成积木拼接但这个比喻有一个前提拼接的几块积木之间不能彼此冲突。例如typeWithId{id:number;};typeWithName{name:string;};typeUserWithIdWithName;这里的组合就很自然因为两个结构互补。但交叉类型不是万能拼接器很多初学者第一次接触时会以为它就是“把两个对象简单合并”。大多数互补场景下确实接近这样但如果字段冲突结果往往没有你想得那么直观。typeA{value:string};typeB{value:number};typeCAB;这里的C[value]不会 magically 变成“字符串或数字”而会进入一种几乎不可满足的状态因为一个值不可能同时既是string又是number。所以一个非常实用的工程判断是交叉类型适合合并互补约束不适合硬拼彼此冲突的模型。type的价值不只是给对象命名很多人最开始把type只当成“另一种写对象的方式”其实它更大的价值在于可以给任意类型表达式起别名。typeIDstring|number;typePoint[number,number];typeHandler(message:string)void;typeRequestStatusidle|loading|success|error;这件事很重要因为一旦类型表达式开始变复杂命名本身就是在提升可读性。对比下面两种写法functionfindUser(id:string|number){}typeUserIdstring|number;functionfindUser(id:UserId){}第二种更易读也更利于复用。因为“联合类型本身”只是语法“命名后的类型别名”才更接近系统里的业务概念。一个典型场景用联合类型表达接口结果假设一个登录接口要么成功要么失败typeLoginSuccess{success:true;token:string;userId:number;};typeLoginFailure{success:false;message:string;};typeLoginResultLoginSuccess|LoginFailure;这就是联合类型最有力量的地方。你不必用一堆可选字段去糊一个宽对象而是明确告诉系统返回值只有两种合法形态。相比下面这种写法typeLoginResult{success:boolean;token?:string;userId?:number;message?:string;};前者的表达能力强得多因为它保留了状态和字段之间的真实关系。一个典型场景用交叉类型叠加通用字段typeEntity{id:number;};typeTimestamps{createdAt:string;updatedAt:string;};typeArticleEntityTimestamps{title:string;content:string;};这类组合在工程里非常常见。它不是为了炫技而是为了减少重复并让公共结构保持统一。如何判断该用联合还是交叉这是一个非常值得反复练习的判断如果你表达的是“几种合法形态之一”用联合类型如果你表达的是“同时具备多个特征”用交叉类型如果一个类型表达式会反复出现给它起一个别名你也可以把它翻译成更口语的版本“或者”通常对应联合“并且”通常对应交叉初学者常见误区误区一联合类型写出来后直接按某一类使用这会立刻遇到报错。不是 TypeScript 在找麻烦而是你还没有告诉编译器当前到底是哪一种情况。误区二交叉类型被当成“对象合并万金油”如果两个模型本身矛盾交叉后的结果只会更难用不会更强大。误区三复杂类型不命名一长串联合、交叉、函数签名如果没有名字代码会很快失去可读性。类型别名很多时候不是可选项而是沟通工具。本文小结联合类型和交叉类型是 TypeScript 表达复杂业务关系的基础工具。联合类型让你描述“这个值可能是哪几种合法形态之一”交叉类型让你描述“这个值必须同时满足哪些约束”。而type作为类型别名则让这些表达可以被命名、复用和沟通。从这一篇开始你应该逐渐放弃“变量是什么类型”的单点思维转向“一个业务对象可能处于哪些合法状态”的系统思维。这才是 TypeScript 真正开始有建模力量的地方。练习定义一个Result类型表示ok或error再扩展成一个真正可用的成功/失败对象联合。定义UserBase和UserProfile然后用交叉类型合成UserDetail。思考登录接口返回成功和失败两种结构时为什么联合类型通常比“一个大对象里全是可选字段”更合适。后记2026年5月21日于上海。