什么是联合类型?
联合类型就是并集,从数学的角度来看:集合 {1, 2, 3} 和 {2, 3, 4} 的并集是 {1, 2, 3, 4}, TS的并集也是一样的,如下图所示:
上图中的 类型C1 就是 A1和B1的并集,写做 A1 | B1,所以 C1 可以是 number也可以是string,
C2也是如此,它是A2和B2的并集,所以它的属性也可以是只有name 或者 age
如何使用联合类型
示例:
// 这个函数里面的参数a是一个联合类型
const f1 = (a: number | string) => {
// 此时怎么使用参数 a 呢?
// 既不能把 a 当作number (调用 toFixed 方法会报错)
// 也不能把 a 当作 string
// a只能调用 number 和 string 的交集
}
- 上面例子中的参数 a 可以是 number 也可以是 string, TS无法判断,它只能调用 number 和 string 类型的交集, 如
a.toString, 除了交集其他的不行。 所以必须判断一下它的类型是什么,这个就叫做类型收窄
使用 typeof 区分
const f1 = (a: number | string) => {
if (typeof a === 'number') {
a.toFixed(2)
} else if (typeof a === 'string') {
parseFloat(a).toFixed(2)
} else {
throw new Error('Never do this')
}
}
- 这种区分也叫类型收窄,包括下面的那些区分方式也是
- 用typeof来判断也是有缺陷的,因为typeof只能判断简单类型,那么其他类型它就判断不了
如下图:
使用 instanceof 来区分类型
const f1 = (a: Array<Date> | Date) => {
if (a instanceof Date) {
a.toISOString()
} else if (a instanceof Array) {
a[0].toISOString()
} else {
throw new Error('Never do this')
}
}
-
instanceof 的局限性
- 不支持 string, number, boolean等基本类型
- 不支持 TS 独有的类型
-
问:下面的例子要用什么来区分?
type Person = {
name: string
}
const f1 = (a: Person | Person[]) {
if (a intanceof Person) {
// 用这个会报错:“Person”仅指类型,但在此处用作值。
} else {
throw new Error('Never do this')
}
}
使用 in 来收窄类型
- 只适用于部分对象,缺陷是很多对象它的属性可能是一样的
type Person = {
name: string
}
const f1 = (a: Person | Person[]) {
if ('name' in a) {
a // Person
} else {
a // Person[]
}
}
-
还有很多的方法可以来判断类型,但是,都有一些缺陷和局限性。上面的说的所有类型收窄都是通过JavaScript来实现的,
-
那么有没有别的区分类型的万全之法呢?
类型谓词 / 类型判断 is
例子如下:
type Rect = {
height: number
width: number
}
type Circle = {
center: [number, number]
radius: number
}
const f1 = (a: Rect | Circle) => {
if (isRect(a)) {
// 鼠标移到a上面,ts会告诉你a的类型是Rect
a
}
if (isCircle(a)) {
a
}
}
// 这里的 x is Rect 是 ts 语法,调用这个函数,如果通过,它就会告诉ts,类型是什么
function isRect(x: Rect | Circle): x is Rect {
return 'height' in x && 'width' in x
// 这里面的判断随意,可以是上面的 typeof, instanceof, in 等等,能判断就行
}
function isCircle(x: Rect | Circle): x is Circle {
return 'center' in x && 'radius' in x
}
-
is的优点是:
- 支持所有的TS类型
-
is的缺点是:
- 麻烦
那有没有更简单的区分类型的方法呢?
可辨别联合
- 我们可以在每个TS类型上加一个共同的属性,kind(随便起,xxx也行),然后判断的时候用kind判断
例子如下:
type Rect = {
kind: 'rect'
height: number
width: number
}
type Circle = {
kind: 'circle'
center: [number, number]
radius: number
}
const f1 = (a: Rect | Circle) => {
if (a.kind === 'rect') {
a
// 类型是Rect
} else if (a.kind === 'circle') {
a
// 类型是Cirle
}
}
问: 那如果联合类型中有简单类型呢?
type Rect = {
kind: 'rect'
height: number
width: number
}
type Circle = {
kind: 'circle'
center: [number, number]
radius: number
}
const f1 = (a: Rect | Circle | string | number) => {
if (typeof a === 'string') {
} else if (typeof a === 'number') {
} else if (a.kind === 'rect') {
a
// 类型是Rect
} else if (a.kind === 'circle') {
a
// 类型是Cirle
}
}
-
优点
让复杂类型的收窄变成简单类型的对比
-
使用可辩别联合的要求
如 T 联合了A,B,C,D : T = A | B | C | D
- 要求A,B,C,D 有相同的属性,可以是kind或者其他
- kind的类型必须是简单类型
- 各类型中的kind可区分
则称 T 为可辩别联合
总结: 要有 同名、可辩别的简单类型的 key
思考
any 是所有类型的联合类型吗? 为什么?
- 这里的所有类型除了 never / unknown / void
const f1 = (a: any) => {
a
// ??
}
我认为:any不是所有类型的联合类型。为什么呢?
我们上面说过,联合类型的调用只能使用它们之间的交集,需要类型收窄了才能使用。而any 呢,它什么都可以调用,所有我认为它不是所有类型的联合类型
const f1 = (a: any) => {
a.push('b')
a.splice(0)
a.toFixed(2)
}
// TS不会报错
什么是所有类型的联合类型呢??
- 我觉得 unknown 是所有类型的联合类型
- 正如上面说的:联合类型的调用只能使用它们之间的交集,需要类型收窄了才能使用。
- unknown 不知道它是什么类型,只要你给它判断一个类型,它就能使用这个类型
例子如下:
const f1 = (a: unknown) => {
if (typeof a === 'string') {
a
// a的类型是 string
} else if (a instanceof Date) {
a
// a 的类型是 Date
}
}