泛型是利用变量来表达类型的方式,在定义函数、接口或者类的时候,先用这个类型变量表示类型,在实际使用的时候才将数据类型传给类型变量,以达到一段程序可以适应多种类型的目的。泛型一般用大写字母表示,最常见的是' T '。
1.在函数中使用泛型
下面函数中的<T>就是一个泛型变量。在函数最前面传入后,函数内部就可以使用这个泛型。例子中我们给T传入了number类型,则getArray的函数在执行时只能传入number类型的参数。
const getArray = <T>(value: T, times: number = 5): T[] => {
return new Array(times).fill(value)
}
console.log(getArray<number>(1)) //
2.使用两个泛型变量
// 两个泛型变量 T 和 U
const getArray = <T, U>(param1: T, param2: U, times: number): [T, U][] => {
return new Array(times).fill([param1, param2])
}
TS编辑器会根据传入的值自动补充T和U传入的值,下面的例子中,T和U分别被隐式赋予了number和string两个类型值。
const arr = (getArray(1,'a',3))
console.log(arr) // [[1,'a'],[1,'a'],[1,'a']]
后续操作中,如果对非number型的变量使用了number型的静态方法,则编辑器会报错
arr[0][1].toFixed() // error
3. 使用泛型定义函数类型
let getArray: <T>(arg: T, times: number) => T[]
getArray = (value: any, times: number ) => {
return new Array(times).fill(value)
}
4. 在类型别名中使用泛型
type GetArray = <T>(arg: T, times:number) => T[]
let getArray: GetArray = (arg: any, times:number) => {
return new Array(times).fill(arg)
}
5.在接口中使用泛型
在接口内部定义:
interface GetArray {
<T>(arg: T, times:number): T[]
}
在接口外层定义,好处是每个属性都可以使用:
interface GetArray<T> {
(arg: T, times:number): T[],
array: T[]
}
- 泛型约束 泛型约束是对泛型的一种限制,如下面的例子,我们先定义一个接口ValueWithLength,然后让泛型T extends ValueWithLength,则根据接口的定义,T现在被限制为传入的类型必须有length属性:
interface ValueWithLength {
length: number
}
const getArray = <T extends ValueWithLength>(arg: T, times): T[] => {
return new Array(times).fill(arg)
}
下面的例子,通过两个泛型的限制,实现了获取对象属性值时的检测功能:
const getProps = <T, K extends keyof T>(object: T, propName: K) => {
return object[propName]
}
const objs = {
a: 'a',
b: 'b'
}
getProps(objs, 'a') // 'a'
getProps(objs, 'c') // error,对象objs上不存在c这个属性