基本定义
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'
进行类型推导:
相较于直接将 value
和 arr
的类型定义为 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 也是可以像函数使用泛型时那样推导出类型而无需明确的传入泛型类型。当然也可以显式的传入,就像定义 addValue
或 addValue1
或 addValue2
时那样。
泛型约束
如果我们直接对一个泛型参数取 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
的所有键的联合类型。