TypeScript——联合类型和类型收窄以及交叉类型

105 阅读2分钟

联合类型(并集 |)

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
}

image.png

类型收窄

想要使用联合类型,就要把类型区分开来,即类型收窄

1. 使用 typeof 收窄类型

const f1 = (a: number | string) => {
  if (typeof a === 'number'){
    a.toFixed(2)
  }else if(typeof a === 'string'){
    parseFloat(a)
  }
}

typeof的局限性

typeof 返回值有哪些? image.png 由此可见 typeof 具有局限性: image.png

2. 使用 instance 收窄类型

const f1 = (a: Array<Date> | Date) => {
  if (a instanceof Date){
    a.toISOString()
  }else if(a instanceof Array){
    a[0].toISOString()
  }
}

instance 的局限性

  1. 不支持 string、number、boolean ......
  2. 不支持 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 | ...

  1. A、B、C、D ... 有相同属性 kind 或其他
  2. kind 的类型是简单类型
  3. 各类型中的 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 // 强制进行收窄
}

联合类型和类型收窄的总结

image.png

交叉类型(交集 &)

type A = string & number
// ^-- never

image.png

模拟 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。