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

相关推荐
MiyueFE21 分钟前
14 个逻辑驱动的 UI 设计技巧,助您改善任何界面
前端·设计
啃火龙果的兔子25 分钟前
前端单元测试覆盖率工具有哪些,分别有什么优缺点
前端·单元测试
「、皓子~1 小时前
后台管理系统的诞生 - 利用AI 1天完成整个后台管理系统的微服务后端+前端
前端·人工智能·微服务·小程序·go·ai编程·ai写作
就改了1 小时前
Ajax——在OA系统提升性能的局部刷新
前端·javascript·ajax
凌冰_1 小时前
Ajax 入门
前端·javascript·ajax
京东零售技术1 小时前
京东小程序JS API仓颉改造实践
前端
奋飛1 小时前
TypeScript系列:第六篇 - 编写高质量的TS类型
javascript·typescript·ts·declare·.d.ts
老A技术联盟1 小时前
从小白入门,基于Cursor开发一个前端小程序之Cursor 编程实践与案例分析
前端·小程序
风铃喵游1 小时前
构建引擎: 打造小程序编译器
前端·小程序·架构
sunbyte2 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)
前端·javascript·css·vue.js·前端框架·tailwindcss