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

相关推荐
常常不爱学习8 分钟前
Vue3 + TypeScript学习
开发语言·css·学习·typescript·html
黄毛火烧雪下20 分钟前
React Native (RN)项目在web、Android和IOS上运行
android·前端·react native
fruge26 分钟前
前端正则表达式实战合集:表单验证与字符串处理高频场景
前端·正则表达式
baozj31 分钟前
🚀 手动改 500 个文件?不存在的!我用 AST 撸了个 Vue 国际化神器
前端·javascript·vue.js
用户40993225021239 分钟前
为什么Vue 3的计算属性能解决模板臃肿、性能优化和双向同步三大痛点?
前端·ai编程·trae
海云前端140 分钟前
Vue首屏加速秘籍 组件按需加载真能省一半时间
前端
蛋仔聊测试41 分钟前
Playwright 中route 方法模拟测试数据(Mocking)详解
前端·python·测试
零号机1 小时前
使用TRAE 30分钟极速开发一款划词中英互译浏览器插件
前端·人工智能
疯狂踩坑人1 小时前
结合400行mini-react代码,图文解说React原理
前端·react.js·面试
Mintopia1 小时前
🚀 共绩算力:3分钟拥有自己的文生图AI服务-容器化部署 StableDiffusion1.5-WebUI 应用
前端·人工智能·aigc