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 的所有键的联合类型。

相关推荐
Ares码农人生16 分钟前
React 前端框架简介
前端·react.js·前端框架
小汤猿人类17 分钟前
nacos-gateway动态路由
java·前端·gateway
GISer_Jing18 分钟前
前端经典面试合集(二)——Vue/React/Node/工程化工具/计算机网络
前端·vue.js·react.js·node.js
GesLuck1 小时前
C#控件开发4—仪表盘
前端·经验分享·c#
小林爱1 小时前
【Compose multiplatform教程14】【组件】LazyColumn组件
android·前端·kotlin·android studio·框架·多平台
过往记忆7 小时前
告别 Shuffle!深入探索 Spark 的 SPJ 技术
大数据·前端·分布式·ajax·spark
高兴蛋炒饭8 小时前
RouYi-Vue框架,环境搭建以及使用
前端·javascript·vue.js
m0_748240449 小时前
《通义千问AI落地—中》:前端实现
前端·人工智能·状态模式
ᥬ 小月亮9 小时前
Vue中接入萤石等直播视频(更新中ing)
前端·javascript·vue.js
神雕杨10 小时前
node js 过滤空白行
开发语言·前端·javascript