TS 中的类型运算:联合类型
TS = JS + 类型系统
JS 可以对值进行加减运算,如果把 TS 的类型系统当作一门语言,TS 也同样可以对类型进行各种运算
联合类型(并集)
英文全称:union types

type A = { name: string } 表示 name 为 string 的所有对象
但不要错误地认为这些对象只有 name 这一个 key
比如对象 { name: 'ClariS'; age: 18 } 也是属于 A 类型的
即 A 类型的对象可以有 age,也可以没有 age
同理,type = { age: number } 表示 age 为 number 的对象,这些对象的 name 可以为空,也可以不为空
如何使用联合类型?
1 | const f1 = (a: number | string) => { |
如果不拆开类型,就只能使用 number 和 string 共同拥有的方法或属性,比如 toString()
因此要使用联合类型,就得先区分类型,也就是进行类型收窄(Narrowing)
如何进行类型收窄?
使用 typeof
1 | const f1 = (a: number | string) => { |
typeof 的局限性:无法区分数组、日期、普通对象以及 null(返回值都是 "object")
使用 instanceof
1 | const f1 = (a: Array<Date> | Date) => { |
instanceof 的局限性:
- 不支持基础类型,
string、number、bigint、boolean、symbol、undefined - 不支持 TS 独有的类型
1
2
3
4
5
6
7
8
9type Person = { name: string };
const f1 = (a: Person | Person[]) => {
if (a instanceof Person) {
// ^--- 报错:type 不能用作 value
} else {
throw new Error('Never do this');
}
};
使用 in 操作符
1 | type Person = { name: string }; |
in 操作符的局限性:只适用于部分对象
比如当判断的两个对象存在相同的 key (存在相同 key,其中一个对象的 key 可能不存在),或不为键值对形式的对象(比如日期、正则、函数等)时,则无法使用 in 操作符来收窄类型
使用 JS 中判断类型的函数
1 | const f1 = (a: string | string[]) => { |
使用逻辑
1 | const f1 = (a?: string[]) => { |
使用类型谓词 is
上述所有的类型收窄方法都是通过 JavaScript 来实现的,有没有区分类型的万全之法?
1 | type Rect = { height: number; width: number }; |
优点:支持所有 TS 类型
缺点:需要自己实现,挺麻烦的
有没有更简单的办法?
可辨别联合
1 | interface Circle { kind: "circle"; radius: number; } |
让复杂类型的收窄变成简单类型的对比
对于类型 T = A | B | C | D | ...,有以下要求:
A、B、C、D...有相同属性kind或其他kind的类型是简单类型- 各类型中的
kind可区分
则称 T 为可辨别联合
一句话总结:具有同名、可辨别的简单类型的 key 的联合类型,称为可辨别联合
使用断言
可以使用 as 进行强制的类型收缩
1 | interface Circle { kind: "circle"; radius: number; } |
any 等于所有类型的联合吗?
这里直接给出结论:any 不等于所有类型的联合
注意:这里的所有类型不包括
neverunknownanyvoid
用反证法可以证明:
- 只要类型发生了联合,就只能使用它们共同拥有的属性或方法(想要使用各自的方法就必须做类型收窄)
- 但是使用
any之后,不做类型收窄可以使用所有类型的方法 - 说明
any不是所有类型的联合
1 | const f1 = (a: string | number) => { |
且 ts 的绝大部分规则对 any 不生效,但有一种特殊情况,any 无法赋值给 never
那什么等于所有类型的联合呢?
1 | const f1 = (a: unknown) => { |
可以看出,unknown 是可以收窄到任何类型的,因此,unknown 就是所有类型的联合