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 不等于所有类型的联合
注意:这里的所有类型不包括
never
unknown
any
void
用反证法可以证明:
- 只要类型发生了联合,就只能使用它们共同拥有的属性或方法(想要使用各自的方法就必须做类型收窄)
- 但是使用
any
之后,不做类型收窄可以使用所有类型的方法 - 说明
any
不是所有类型的联合
1 | const f1 = (a: string | number) => { |
且 ts 的绝大部分规则对 any
不生效,但有一种特殊情况,any
无法赋值给 never
那什么等于所有类型的联合呢?
1 | const f1 = (a: unknown) => { |
可以看出,unknown
是可以收窄到任何类型的,因此,unknown
就是所有类型的联合