联合类型(并集 |)
type A1 = number
type B1 = string
type C1 = A1 | B1
const c1: C1 = 42
type A2 = { name: string }
type B2 = { age: number }
type C2 = A2 | B2
const c2: C2 = {
name: 'Bumble',
age: 20
}
类型收窄
想要使用联合类型,就要把类型区分开来,即类型收窄。
1. 使用 typeof 收窄类型
const f1 = (a: number | string) => {
if (typeof a === 'number'){
a.toFixed(2)
}else if(typeof a === 'string'){
parseFloat(a)
}
}
typeof的局限性
typeof 返回值有哪些?
由此可见 typeof 具有局限性:
2. 使用 instance 收窄类型
const f1 = (a: Array<Date> | Date) => {
if (a instanceof Date){
a.toISOString()
}else if(a instanceof Array){
a[0].toISOString()
}
}
instance 的局限性
- 不支持 string、number、boolean ......
- 不支持 TS 独有的类型
3. 使用 in 收窄类型
只适用于部分对象。
type Person = {
name: string
}
const f1 = (a: Person | Person[]) => {
if ('name' in a){
a // Person
}else{
a // Person[]
}
}
以上所有类型收窄都是通过 JavaScript 实现
4. is 类型谓词/类型判断(万全之法)
优点: 支持所有TS类型
缺点: 麻烦!
type Rect = { height: number, width: number}
type Circle = { center: [number,number], radius: number}
function isRect(x: Rect | Circle): x is Rect{ //不写bool,写 x is Type
return 'height' in x && 'width' in x
}
const f1 = (a:Rect | Circle) => {
if (isRect(a)){ //判断 a是不是 Rect
a // Rect
}else{
a // Circle
}
}
5. 可辨别联合(用 x.kind 区分类型)
优点: 让复杂类型的收窄变成简单类型的对比。
可辨别联合的要求:
例:T = A | B | C | D | ...
- A、B、C、D ... 有相同属性 kind 或其他
- kind 的类型是简单类型
- 各类型中的 kind 可区分
一句话总结:同名、可辨别的简单类型的key
interface Circle { kind: "circle", radius: number}
interface Square { kind: "square", sideLength: number}
type Shape = Circle | Square // Shape即为可辨别联合
const f1 = (shape: Shape) => {
if (shape.kind === 'circle'){
shape // Circle
}else if (shape.kind === 'square'){
shape // Square
} else {
shape // never
}
}
6. 断言 as
强制进行收窄
interface Circle { kind: "circle", center: [number,number]}
interface Square { kind: "square", sideLength: number}
type Shape = Circle | Square
const f1 = (a:Shape) => {
(a as Circle).center // 强制进行收窄
}
联合类型和类型收窄的总结
交叉类型(交集 &)
type A = string & number
// ^-- never
模拟 User 继承 Person
type Person = {
name: string
age: number
}
type User = Person & { //模拟 User 继承 Person
id: number
email: string
}
const u: User = {
name: 'Bumble',
age: 18,
id: 1,
email: '666.com'
}
属性冲突:
type 属性冲突不报错,interface 报错。
type Person = {
id: string //属性冲突,不报错
}
type User = Person & {
id: number //属性冲突,不报错
}
const u: User = {
id: 1, //never
}
interface Person {
id: string //属性冲突,报错
}
interface User extends Person{
// ^-- 'number' is not assignable to 'string'
id: number //属性冲突,报错
}
结论
交叉类型常用于有交集的类型A、B,如果A、B无交集,可能得到 never,也可能只是属性为 never。