• TS = JS + 类型系统
  • TS类型系统的运算主要有两种:或(|)运算与(&)运算

1 联合类型(并集)

1.1 示例

  • | 可以属于x类型,也可以属于y类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
type A = number
type B = string
type C = A | B
const c1: C = 42
const c2: C = '42'

type A = { name: string }
type B = { age: number }
type C = A | B 
const c1: C = { name: 'gsq' }
const c2: C = { name: 'gsq', age: 10 }

1.2 类型收窄

1.2.1 JS中的类型区分

  • 使用联合类型就涉及到类型的区分,也就是类型收窄(Narrow)

    • 使用typeof

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      const f1 = (a: number | string) => {
      	if(typeof a === 'number'){
      		a.toFixed(2)
      	} else if (typeof a === 'string') {
      		a.split('')
      	} else {
      		throw new Error('Never do this')
      	}
      }
      
      • 缺点:无法区分DatenullArray和普通对象(都是object
    • 使用instanceof

      • 不支持TS独有的类型(会被擦除)
      1
      2
      3
      4
      
      type Person = { name: string }
      const f1 = (a:Person | Person[]) => {
          if(a instanceof Person) // X 报错
      }
      
      • 不支持stringnumberboolean
    • 使用in

      1
      2
      3
      4
      
      type Person = { name: string }
      const f1 = (a:Person | Person[]) => {
          if('name' in a) 
      }
      
      • 只适用于部分普通对象
    • 使用类的判断类型方法:Array.isArray()

1.2.2 类型谓词/类型判断

  • TS中区分类型的万全之法一:类型谓词/类型判断
  • 为了解决以上方案的通病:不支持TS独有的类型,需要使用类型谓词is搭配自定义类型判断函数
  • 说白了就是把类型判断函数提取出来,加上is,使这个方法得到TS支持
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type Rect = {
	height: number
	width: number
}
type Circle = {
	center: [number, number]
	radius: number
}
// 不能用boolean替代x is Rect
// 因为这是给TS看的
const isRect = (x: Rect | Circle): x is Rect => {
    // 这里可以使用js的类型区分方法
    return 'height' in x
}
const isCircle = (x: Rect | Circle): x is Circle => {
    return 'center' in x
}

// 使用
const f1 = (a: Rect | Circle) => {
    if (isRect(a)) {
        a
    }
}

1.2.3 可辩别联合

  • 同名、可辩别的简单类型的key,则称作可辩别联合
  • 代码看起来比较傻
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
type Rect = {
	height: number
	kind: 'Rect'
}
type Circle = {
	kind: 'Circle'
	radius: number
}
const f1 = (shape: Circle | Rect) => {
    if(shape.kind === 'Circle'){}
    else {}
}

1.3 法外狂徒any

  • any是所有类型(never、unknown、any、void除外)的联合吗?为什么?
  • 不是,联合类型必须做类型收窄后才能正常使用,否则只能使用联合类型共有的属性
  • 而any是不需要的,可以随意使用各种属性,TS不会报错的
  • TS大部分规则对any不生效

1.4 重新认识unknown

  • unknown是所有类型(never、unknown、any、void除外)的联合类型
  • 只要做类型收窄,就可以正常使用

2 交叉类型(交集)

2.1 理解type A = { name: string }

  • 表示A类型是一个具有name属性,且值为string类型的对象

  • 注意措辞,是具有,而不是只有,因此以下都属于A类型

    • { name: 'gsq', age: 10 }
    • { name: 'gsq', gender: 'man' }
  • 注意当初始化时,TS会报错,但在赋值时是不会的

1
2
3
4
5
6
type A = { name: string }
// 初始化会报错
const a: A = { name: 'gsq', age: 15 } 
// 赋值不会报错
const b = { name: 'gsq', age: 15 }
const c: A = b 

2.2 交叉类型示例

  • & 既属于x类型又属于y类型
1
2
3
interface Colorful { color: string }
interface Circle { radius: number }
type ColorfulCircle = Colorful & Circle

2.3 属性冲突(无交集)

  • 示例
1
2
3
4
5
6
7
type A = { id: string; name: string }
type B = { id: number; age: number }
type C = A & B
const c:C = {  name: 'gsq', age: 10, id: 1 }// id为never 会报错

interface A { id: string; name: string }
interface B extends A { id: number }// 在定义时就报错
  • 使用type + &在使用时才会报错
  • 使用interface + extends在定义时就会报错

2.4 总结

  • 交叉类型常用于有交集的类型A、B

  • 如果A、B无交集,可能得到never,或者属性为never