006_TypeScript 第六章

descript

  • 这一章我们来聊一下 TS 内的高级类型
  • 其实就是把一些基础的东西进行各种各样的组合, 一些相对高级的用法
  • 在之前几章里面我们也或多或少的提到过某些内容
  • 现在就来汇总专门说一下这个事情

交叉类型( Intersection Types )

  • 这个就和我们运算符里面的 与( && ) 是一样的, 既要 … 又要 … 还要 … O(∩_∩)O~
  • 使用 & 作为交叉符号

descript

  • 一个小栗子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 对象接口 1
    interface One {
    name: string
    age: number
    }

    // 对象接口 2
    interface Two {
    gender: string
    classRoom: number
    }

    // 简单准备一个函数
    function combine(o1: One, o2: Two) {
    const result = {
    ...o1,
    ...o2
    }

    return result
    }
    • 我们看到, 一个简单的功能函数就是把两个对象合并到一个对象
    • 我们给到参数进行了限制, 但是返回值咋办呢 ?
    • 这个时候就可以用到交叉类型了
    1
    2
    3
    4
    5
    6
    7
    8
    function combine(o1: One, o2: Two): One & Two {
    const result = {
    ...o1,
    ...o2
    }

    return result
    }
    • 这样的话, 就是返回值既要满足 One 的接口要求, 也要满足 Two 的接口要求

    descript

    descript

    • 你看, 少了任何一个都不行
    • 其实就是 与 的关系 O(∩_∩)O~

联合类型(Union Types)

  • 这个就和我们运算符里面的 或( || ) 是一样的, 哪一个都行 O(∩_∩)O~

  • 使用 | 作为联合符号

  • 很多时候, 我们在定义一些内容的时候, 可能需要使用到多种都可以, 不管是类型, 包括字面量约束也是一样的

    1
    2
    3
    4
    5
    let foo: number | string = 100

    foo = 'hello world'

    foo = 200
    • 我们给 foo 赋值为 number 类型或者 string 类型都是可以的
  • 字面量也是可以联合的

    1
    let color: 'yellow' | 'blue' | 'orange'

    descript

  • 这就是联合类型

类型保护( Type Guards )

  • 先来看一个案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    interface One {
    name: string

    sayHi(): void
    }

    interface Two {
    name: string

    play(): void
    }

    const user1: One = {
    name: 'Jack',

    sayHi: () => console.log('hello world')
    }

    const user2: Two = {
    name: 'Rose',

    play: () => console.log('basketball')
    }

    function util(user: One | Two): void {
    console.log(user.name)
    user.sayHi()
    user.play()
    }

    util(user1)
    util(user2)

    descript

    • 我们发现里面报错了
    • 这是因为, 两个接口里面都有 name 属性, 所以调用 name 属性没问题
    • 但是你得 user 不确定是 One 还是 Two 类型
      • 调用 sayHi 的活, 如果是 Two 类型就没有, 就会提示错误
      • 调用 play 的话, 如果是 One 类型就没有, 就会提示错误
    • 一旦联合以后, 我们就只能访问两个类型公共的数据
  • 可能有同学想到判断一下不就好了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function util(user: One | Two): void {
    console.log(user.name)

    if (user.sayHi) {
    user.sayHi()
    } else {
    user.play()
    }
    }

    descript

    • 依旧是一个提示错误的状态
  • 这个时候就需要用到断言来对类型进行一次保护

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function util(user: One | Two): void {
    console.log(user.name)

    if ((user as One).sayHi) {
    ;(user as One).sayHi()
    } else {
    ;(user as Two).play()
    }
    }
    • 当我在访问一些非公共属性的时候, 进行一下断言, 确定它一定会有这个属性我才访问
    • 断言也有另外一种写法, 也是可以的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function util(user: One | Two): void {
    console.log(user.name)

    if ((<One>user).sayHi) {
    ;(<One>user).sayHi()
    } else {
    ;(<Two>user).play()
    }
    }
  • 这两种书写方式是一样的, 都是可以实现类型保护

自定义类型保护

  • 通过刚才的案例, 我们发现可以解决问题, 但是很麻烦

  • 因为要不停的写一些断言来验证一下

    descript

  • 随着后面的使用, 用的越多, 需要写断言的地方就越多, 少一个都不行

  • 这个时候, 我们就可以自己写一个类型验证函数, 作用就是专门用于检查类型

  • 先来定义一个类型验证函数

1
2
3
function isOne(pet: One | Two): pet is One {
return (<One>pet).sayHi !== undefined
}
  • 这里的返回值设置, pet is One 是 TS 的语法, 表示 pet 是不是 One 这个类型, 结果必然是一个布尔值, true 或者 false
    • 注意 : 这里的 pet 必须是该函数的形参才可以哦
  • 后面使用的时候, 直接使用 isOne 函数就可以了哦
1
2
3
4
5
6
7
function util(user: One | Two): void {
if (isOne(user)) {
user.sayHi()
} else {
user.play()
}
}