TS学习笔记9-泛型

207 阅读2分钟

在 TypeScript 中,泛型(Generics)允许我们编写可以适用于多种类型的可重用代码。使用泛型可以增加代码的灵活性和可重用性。

要使用泛型,可以在函数、类或接口的定义中使用尖括号(<>)来指定泛型参数。

泛型基本使用

type a = <T>(arg: T) => T
interface b<T> {
  (arg: T): T
}
const a: a = (arg) => arg
const b: b<number> = (arg) => arg
const aa = a('str') // str
const bb = b(1)  // 1

如果类型不对就会报错

function c<T>(arg: T): T {
  return arg
}
const cc: string = c(1) // 报错
const cc = c<string>(1)  // 报错

image.png

image.png

我们在使用的时候可以有两种方式指定类型。

  • 定义要使用的类型 c<string>('str')
  • TS 类型推断,自动推导出类型 c('str')

添加默认参数

可以给泛型添加默认参数

// 错误例子
interface a<T = string> {
  (arg: T): T
}
const a: a = (arg) => arg
const aa = a(1)  // 报错类型“number”的参数不能赋给类型“string”的参数。

// 正确例子
interface a<T = string> {
  (arg: T): T
}
const a: a<number> = (arg) => arg
const aa = a(1)

可以看到当给泛型一个默认类型时,你没有在使用时传入类型 那么就使用的时默认类型。

多个泛型

经典例子,一个函数,传入一个只有两项的元组,交换元组的第 0 项和第 1 项,返回这个元组。

function a<T, U>(arr: [T, U]): [U, T] {
  return [arr[1], arr[0]]
}
a([1, '2'])

函数副作用操作

泛型不仅可以很方便地约束函数的参数类型,还可以用在函数执行副作用操作的时候。

比如我们有一个通用的异步请求方法,想根据不同的 url 请求返回不同类型的数据。

interface userInfo {
  name: string
}
function request<T>(url: string): Promise<T> {
  return fetch(url).then(res => res.json())
}
request<userInfo>('user/info').then(res => {
  console.log(res.name)
})
type x = <T>(url: string) => Promise<T>
const request2: x = (url: string) => {
  return fetch(url).then(res => res.json())
}
request2<userInfo>('user/info').then(res => {
  console.log(res.name)
})

约束泛型

有时候,你需要约束传入的类型具有某些属性,那么就需要用继承来约束泛型

interface Ilength {
  length: number
}

function a<T>(arg: T) {
  return arg.length // 报错 类型“T”上不存在属性“length”。
}

因此需要给T增加限制,可以和 interface 结合,来约束类型。只要是用于length属性的值,都可以当作入参,没有length属性的值会被校验。

interface Ilength {
  length: number
}

function a<T extends Ilength>(arg: T) {
  return arg.length
}
class A {
  length: number
  constructor() {
    this.length = 1
  }
}
a('')
a([])
a({ length: 1 })
a(new A())
a(1) //报错 类型“number”的参数不能赋给类型“Ilength”的参数。

泛型的应用

  • 约束类

特别注意的是,泛型无法约束类的静态成员

class Text<T> {
  private typeList: T[] = []
  push(type: T) {
    this.typeList.push(type)
  }
  pop() {
    return this.typeList.pop()
  }
}
const color = new Text<string>();
color.push(1) // 类型“number”的参数不能赋给类型“string”的参数
  • 约束接口
interface Info<T, U> {
  name: T
  id: U
}
const a: Info<string, number> = { name: 'aaa', id: 2 }
const b: Info<string, string> = { name: 'bbb', id: 'b' }
  • 约束数组
const a: string[] = ['1']
const b: Array<string> = ['1']