联合、交叉与字面量类型
本节目标
- 用联合类型
A | B表达「多种可能」 - 用交叉类型
A & B合并结构 - 理解字面量类型与「字面量收窄」
联合类型表示「值可以是这些类型之一」:
// 运行环境:Node.js 16+ + typescript;类型检查 npx tsc --noEmit ts-l7.ts
// 一个值可能是 string 也可能是 number
function formatId(id: string | number): string {
return `ID:${id}`
}
formatId('a1') // OK
formatId(100) // OK
// 字面量联合:比「string」更精确,只允许这几个值
type Direction = 'up' | 'down' | 'left' | 'right'
function move(dir: Direction): void {
console.log(`move ${dir}`)
}
move('up') // OK
// move('top') // ❌ Argument of type '"top"' is not assignable to 'Direction'交叉类型把多个结构「合并」成一个拥有全部字段的类型:
type HasName = { name: string }
type HasAge = { age: number }
type Person = HasName & HasAge // 同时拥有 name 和 age
const p: Person = { name: 'Tom', age: 18 }字面量类型 + 收窄:TS 会根据「运行时的判断」自动把宽泛类型收窄成具体类型,这叫「类型守卫」:
function print(v: string | number): void {
if (typeof v === 'string') {
console.log(v.toUpperCase()) // 这里 v 被收窄为 string,能用字符串方法
} else {
console.log(v.toFixed(2)) // 这里 v 被收窄为 number
}
}名词解释
联合类型(Union):「或」的关系,写法是 A | B,表示「这个值要么是 A,要么是 B」。它精准表达了现实里大量「不一定」的情况(比如接口字段可能是 string | null)。比一刀切写 any 安全太多。
字面量类型(Literal Type):把「具体的值」当类型用,如 'up' 这个字符串本身就是一种类型(不是泛指 string)。配合联合 'up' | 'down' 能做出「只允许这几个固定值」的枚举级约束,是 TS 强大的来源之一。
课后练习
练习 1:定义类型 Theme = 'light' | 'dark',写一个 applyTheme 函数,非法的主题要编译报错。
答案:
type Theme = 'light' | 'dark' function applyTheme(t: Theme): void { console.log(t) } applyTheme('light') // OK;applyTheme('blue') 报错
练习 2:type A = { x: number }; type B = { x: string }; type C = A & B,C 的 x 是什么类型?为什么?
答案:
C的x是number & string,即「既是 number 又是 string」——这是不可能的空类型(never 的近亲)。说明交叉了两个「同名字段但类型冲突」的结构会得到不可能满足的类型,编译期就会提示矛盾。
总结
我的观点:联合类型是「诚实」的类型——它逼你承认「这里可能不止一种情况」,从而逼你在代码里 if 判断处理每种可能,这正是少 bug 的核心。交叉类型则像「拼乐高」,把小结构组合成大结构,特别适合「配置对象 + 必填项」这类场景。但要警惕:交叉类型遇到「同名字段类型冲突」会产生不可能类型,这往往是你设计出错的信号,而不是 TS 的 bug。善用字面量联合替代魔数字符串,是我能给新手最有用的一个建议——它几乎零成本却能让一整类拼写错误消失。