typescript基础篇(6):泛型

时间:2022-07-23
本文章向大家介绍typescript基础篇(6):泛型,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

6.泛型

软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,在定义函数,接口或类的时候,不预先指定类型,而是等到使用时才指定——这在创建大型系统时为你提供了十分灵活的功能。

在TypeScript中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。

6.1 泛型的场景

举例说,一个打印函数:

const log = (value: string): string => {
  console.log(value)
  return value
}

如果我希望这个函数能接收字符串参数,又能接收字符串数组参数。应当如何实现呢?

现在有两种解决方式:我们已经函数重载,联合类型。

// 1. 函数重载
function log(value: string): string
function log(value: string[]): string[]
function log(value: any): any {
  if (typeof value == "string") {
    console.log(value)
  } else if (Array.isArray(value)) {
    value.forEach((item) => console.log(item))
  }
  return value
}
log("aaa")
log(["bbb", "ccc"])


// 2. 联合类型 相对简洁
const log = (value: string | string[]): string | string[] => {
  if (typeof value == "string") {
    console.log(value)
  } else if (Array.isArray(value)) {
    value.forEach((item) => console.log(item))
  }
  return value
}

log("aaa")
log(["bbb", "ccc"])

但是以上实现不管哪一种,都有重大问题:我们无论用any还是用联合类型:都忽略了入参必须和返回值类型一致。

这时的最好解决方案就是泛型。接下来就改造下:

const log = <T>(value: T): T => {
  console.log(value)
  return value
}

改造之后,T不需要预先指定,也可以准确反映参数和返回值的关系。

6.2 泛型函数调用

泛型函数有两种调用方式:

log("aaa")
log<string[]>(["aaa", "bbb"])

不仅可以用泛型定义函数还可以定义泛型函数类型,格式和函数签名差不多:

const log = <T>(value: T): T => {
  console.log(value)
  return value
}

type Log = <T>(value: T) => T
const myLog: Log = log

6.3 泛型接口

泛型接口:

interface Log {
  <T>(value: T): T
}

当你吧<T>放到Log后面,接口的所有成员都会收到<T>的约束

interface Log<T> {
  (value: T): T
}
// const myLog: Log = log //报错:需要指定类型参数
const my_log: Log<number> = log
my_log(1) // my_log只接收数字

6.4 泛型类

泛型类看上去与泛型接口差不多。泛型类使用( <>)括起泛型类型,跟在类名后面。

class Log<T> {
  run(value: T) {
    console.log(value)
  }
}

const log1 = new Log<number>()
log1.run(1)

// 不指定方法, 传入的是任意的值。
const log2 = new Log()

注意,泛型不得用于静态成员(static声明的成员)

6.5 类型约束

有时候我们会希望实现类似这样的功能:

const log = <T>(value: T): T => {
  console.log(value, value.length)
  return value
} // 执行报错

允许传入一个任意类型的参数,还要可以打印length属性。这种逻辑别说在ts,在js也算不得很好的实践。

这时,就需要类型约束。我们定一个length接口,表示定义了一个包含length属性的类型,然后让泛型参数继承这个接口:

interface Length {
  length: number
}

const log = <T extends Length>(value: T): T => {
  console.log(value, value.length)
  return value
}

这时候的泛型T就不是什么都能传了。能传的包括数组,字符串,{length:number等}