【Typescript小手册】高级类型

·  阅读 320

概述

Typescript 的高级类型指操作基本类型和复合类型而得的类型,包括:

  • 联合
  • 交叉
  • 泛型
  • 字面量类型

类型别名

在讲具体的高级类型之前,我们先了解一下 Typescript 的类型别名。类型别名也是一种类型,用一个单词代表可能比较复杂的类型声明,用关键字type表示。

示例:

type S = string

let a: S = 'a'
复制代码

这里用S作为string的别名,使用方法和string一模一样。

别名不仅可以代表基本类型,它可以代表任意类型。示例:

type SA = string[] // 代表字符串数组
type Handler = (x: string) => void // 代表函数
type I = {
  // 代表接口
  name: string
  value: number
}
class C {
  name: string = 'a'
  value: number = 0
}
type D = C // 代表类
复制代码

类型

联合

联合类型是指变量为多个类型中的一种,是“或”的关系,用操作符|表示。

示例:

type StringOrNumber = string | number

let a: StringOrNumber = 'a'
let b: StringOrNumber = 0

function log(x: string | number) {
  console.log(x)
}
复制代码

这里StringOrNumber表示一种可以是字符串或者也可以是数字的类型,所以对于StringOrNumber类型的变量,既可以赋值为字符串,也可以赋值为数字。

在 Typescript 中,nullundefined与其它类型是同层级的,它们不是任何类型的子类型,也就是说我们不能用它们来表示一个特定类型的空指针。但是我们确实需要在一个变量未确定时保持为空状态,这时可以用联合类型来表示一个变量可为nullundefined。示例:

interface A {
  name: string
}

let a: A | null = null
复制代码

三目条件运算符

三目条件运算符表达式的返回值类型可能是一个联合类型。

示例:

let a = Math.random() < 0.5 ? 'a' : 0 // string | number
复制代码

因为三目条件运算符表达式可能返回字符串或数字,所以其返回值的类型是string | number

交叉

交叉类型是指多个类型合并成一个类型,是“且”的关系,用操作符&表示。

示例:

interface A {
  x: string
  y: number
}
interface B {
  x: string
  z: number
}

type C = A & B

let c: C = { x: 'x', y: 0, z: 1 }
复制代码

交叉类型的成员包含了所有原类型的的所有成员,比如上述代码中,c变量必须既是A类型也是B类型,也就是说它必须同时拥有两个类型要求的属性。

有一些类型是无法交叉的,比如基本类型。示例:

type A = string & number
复制代码

因为一个变量不可能既是字符串又是数字,所以最终A类型是never类型。

如果被交叉的两个类型有同名但类型不同的成员,那么这两个同名成员也会被交叉。示例:

interface A {
  x: { a: string; b: number }
}
interface B {
  x: { a: string; c: number }
}

type C = A & B

let c: C = { x: { a: 'a', b: 0, c: 1 } }
复制代码

类型AB有同名但类型不同的成员x,交叉时两个x成员也可以交叉,因此最终x的类型为:{a: string, b: number, c: number}

字面量类型

Typescript 中字面量也可以作为一个类型。比如:

let a: 'A'
复制代码

上述声明表示变量a的值只能是'A',赋值为其它值时会报错。

此处要注意与变量A的区别,即:

const A = 'A'
type A = 'A'
复制代码

const A = 'A'表示变量 A 的值是'A',而type A = 'A'表示一种类型,这个类型的变量只能被赋值为A两者的使用场景不同

数字和布尔也可以作为字面量类型。这看起来没什么用,但是这种特性一般会与联合结合起来使用。比如:

type Option = 'A' | 'B' | 'C' | 'D'

let a: Option = 'A'
let e: Option = 'E' // 错误
复制代码

Option类型的变量只能被赋值为'A''B''C''D'其中一个值。这一种用法和枚举很像,比如:

enum Option {
  A,
  B,
  C,
  D
}
复制代码

不同的是,字面量类型本质上是字符串,可以使用字符串的所有方法。而且,通过keyof操作符,我们可以动态的生成字面量类型,这一点将在之后介绍。

泛型

Typescript 中的泛型与其它面向对象语言中的泛型和相似,包括:泛型函数、泛型类和泛型接口。

泛型函数

示例:

function merge<T, U>(x: T, y: U): { x: T; y: U } {
  let t: { x: T; y: U } = { x, y }

  return t
}

merge<string, number>('a', 0)
复制代码

我们可以在中括号内声明函数中使用到的泛型变量,它代表了在调用函数时传入的类型。类型变量T可以用在任何需要类型声明的地方,比如参数类型、返回值类型、局部变量类型等。

箭头函数也可以包含泛型。示例:

const merge = <T, U>(x: T, y: U): { x: T; y: U } => {
  let t: { x: T; y: U } = { x, y }

  return t
}
复制代码

泛型类

示例:

class Merge<T, U> {
  x: T
  y: U

  constructor(x: T, y: U) {
    this.x = x
    this.y = y
  }

  merge(): { x: T; y: U } {
    let t: { x: T; y: U } = { x: this.x, y: this.y }

    return t
  }
}

let merge = new Merge<string, number>()
复制代码

我们可以在中括号内声明类中使用到的泛型变量,它代表了在实例化类时传入的类型。类型变量T可以用在任何需要类型声明的地方,比如成员、构造方法参数类型等。

泛型接口

示例:

interface Merge<T, U> {
  x: T
  y: U
}

let merge: Merge<string, number> = { x: 'a', y: 0 }
复制代码

我们可以在中括号内声明接口中使用到的泛型变量,它代表了在声明变量类型时传入的类型。类型变量T可以用在任何需要类型声明的地方,比如成员、方法参数类型等。

泛型约束

由于在泛型类型中,泛型变量是在使用时传入的,所以在编译时无法得知其具体类型,那么读取类类型变量的任何成员都是不行的。比如:

function scale<T>(x: T): number {
  return x.length // 错误
}
复制代码

虽然我们可能心里知道在使用时我们会传入一个具有length成员的变量(比如字符串),但是在编译时编译器并不知道。因此,我们需要告诉编译器T类型是那一类具有length成员的类型。使用extends关键字:

interface HasLength {
  length: number
}

function scale<T extends HasLength>(x: T): number {
  return x.length
}

scale('abc')
复制代码

我们使用extends对类型变量T进行了约束,它必须具有length属性。也就是是说T类型必须实现或继承了 HasLength 类型。

默认类型

泛型参数表可以有默认类型,声明方法和默认参数类似。

示例:

function print<T, U = number>(x: T, y: U) {
  // ...
}

print<string>('x', 0)
复制代码

同样的,默认类型参数后的类型参数必须也有默认类型。

预置泛型

Typescript 中的一些更高级用法是通过泛型实现的,这些泛型预置在编译器原生库中,比如获取函数返回值类型:

function scale(x: string): number {
  return x.length
}

let a: ReturnType<typeof scale> = scale('a')
复制代码

上面的示例中,先通过typeof操作符获取scale函数的类型,再通过预置泛型ReturnType获取了这个函数类型的返回值类型。

更多的预置泛型可以参考 Typescript 文档utility-types

分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改