TS 中的泛型

基本定义

typescript 中的泛型指在定义函数、接口或类的时候,无法预先确定具体的类型,而是等到使用的时候再指定具体类型的一种特性。简单点说就是类型的参数化。

函数中使用泛型

比如,现在定义一个函数,用于生成一个包含指定数量(count)的指定内容(value)的数组,内容的类型如果写死,则一旦传入的 value 是其它类型的,该函数就不能用了,这里可以用泛型来解决,在函数名后面加个<>里面写个 T (或其它任意的变量,一般都是写 'T',可以理解为 type):

typescript 复制代码
function makeArr<T>(value: T, count: number) {
  const arr: T[] = []
  for (let index = 0; index < count; index++) {
    arr.push(value)
  }
  console.log(arr)
  return arr
}

makeArr<string>('hello', 3) // [ 'hello', 'hello', 'hello' ]
makeArr<number>(1, 3) // [ 1, 1, 1 ]

在调用 makeArr 的时候,可以明确的通过 <string> 传入类型,也可以不写 <string>,通过传入的 'hello' 进行类型推导:

相较于直接将 valuearr 的类型定义为 any,用泛型的好处之一在于,会有智能提示,比如对数组的某一项进行操作的时候,会判断出该项的类型,然后提示该类型的方法:

多个泛型参数的函数

函数中有多个泛型的参数

typescript 复制代码
function fn<K, V>(a: K, b: V) {
  console.log([a, b])
  return [a, b]
}

fn<string, number>('1', 1)
fn('1', 1) // 类型推导

至于 K(key) 或 V(value) 或之前用过 T(type),则是可以随便是什么其它的大写字母,只是一般习惯用这几个。

箭头函数传递泛型

如果是箭头函数,泛型的写法如下:

typescript 复制代码
const fetchGoodData = <T>() => {
  return qyRequeset.get<T>('/api-renjian/index.php?type=json')
}

泛型接口

在定义接口时,为接口中的属性或方法定义泛型类型,在使用接口时,再指定具体的泛型类型。

typescript 复制代码
interface IBase<T> {
  arr: Array<T>
  add: (t: T) => void
  getByName: (name: string) => T
}

class Singer {
  constructor(public name: string, public age: number) { }
}

class SingerCR implements IBase<Singer>{
  arr: Array<Singer> = []
  add(singer: Singer) {
    this.arr.push(singer)
  }
  getByName(name: string): Singer {
    //  <Singer> 类型断言,否则报错:不能将类型 "Singer | undefined" 分配给类型 "Singer"。不能将类型 "undefined" 分配给类型 "Singer"。
    return <Singer>this.arr.find((item) => item.name === name)
  }
}

const singerCR = new SingerCR()
singerCR.add({
  name: 'Jay',
  age: 22
})

console.log(singerCR.getByName('Jay')) // {name: "Jay", age: 22}

泛型默认值

注意接口使用泛型的时候,如第 11 行的 IBase<Singer> 中的 <Singer> 是不能省略的,ts 不会进行类型推导。如果想省略,可以在第 1 行定义接口时,给泛型一个默认值:

typescript 复制代码
interface IBase<T = Singer> {
  // ...
}

泛型类

定义一个类,类中的属性或是方法的参数/返回值的类型是不确定的,定义为泛型类型。

typescript 复制代码
class AddValue<T> {
  constructor(public value: T) { }
  add?: (x: T, y: T) => T
}

const addValue: AddValue<number> = new AddValue<number>(1) // 完整的写法
const addValue1: AddValue<number> = new AddValue(1)
const addValue2 = new AddValue<number>(1)
const addValue3 = new AddValue(1) // 类型推导

new 一个类的时候,ts 也是可以像函数使用泛型时那样推导出类型而无需明确的传入泛型类型。当然也可以显式的传入,就像定义 addValueaddValue1addValue2 时那样。

泛型约束

如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性,所以可以使用 extends 让泛型继承某个接口从而对泛型的类型进行约束:

typescript 复制代码
interface ILength {
  length: number
}
function fn<T extends ILength>(x: T) {
  console.log(x.length)
}

fn('ab') // 2

更进一步,下面的例子中,通过 keyof 关键字,让 P 必须是类型 O 的所有的键中的某一个:

typescript 复制代码
function fn<O, P extends keyof O>(obj: O, prop: P) {
  console.log(obj[prop])
}

P 相当于是类型 O 的所有键的联合类型。

相关推荐
SameX5 分钟前
初识 HarmonyOS Next 的分布式管理:设备发现与认证
前端·harmonyos
M_emory_31 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito35 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员1 小时前
响应式网页设计--html
前端·html
mon_star°1 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184552 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
文军的烹饪实验室3 小时前
ValueError: Circular reference detected
开发语言·前端·javascript
Martin -Tang4 小时前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发4 小时前
解锁微前端的优秀库
前端
王解5 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js