泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
函数泛型
本来函数参数类型的不同时,要写多个,现在用泛型,只需要使用时传参即可
- 单个类型参数
function str(a: string, b: string): Array<string> {
return [a, b]
}
function num(a: number, b: number): Array<number> {
return [a, b]
}
str('1', '2')
num(1, 2)
function fn<T>(a: T, b: T): Array<T> {
return [a, b]
}
fn(1,2)
fn('1','2')
- 多个类型参数
function fn<T, U>(a: T, b: U): Array<T | U> {
return [a, b]
}
fn(1, true)
fn('1', '2')
类型别名泛型
type A<T> = string | number | T
let a: A<boolean> = true
let a1: A<undefined> = undefined
泛型接口
interface Person<T, U> {
name: T
age: U
}
let p: Person<string, number> = {
name: 'zs',
age: 18,
}
泛型约束
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法
function fn<T>(a: T) {
console.log(a.length)
// error TS2339: Property 'length' does not exist on type 'T'
}
fn('111')
fn([1, 2, 3])
这时,我们可以对泛型进行约束,只允许这个函数传入那些包含 length 属性的变量。这就是泛型约束
interface Len {
length: number
}
function fn<T extends Len>(a: T) {
console.log(a.length)
}
fn('111')
fn([1, 2, 3])
使用keyof
约束对象
其中使用了TS泛型和泛型约束。首先定义了T
类型并使用extends
关键字继承object
类型的子类型,然后使用keyof
操作符获取T
类型的所有键,它的返回类型是联合类型,最后利用extends
关键字约束 K
类型必须为keyof T
联合类型的子类型
let person = {
name: 'zs',
age: 18,
}
function fn<T, K>(obj: T, key: K) {
console.log(obj[key])
// error TS2536: Type 'K' cannot be used to index type 'T'
}
let person = {
name: 'zs',
age: 18,
}
function fn<T extends object, K extends keyof T>(obj: T, key: K) {
console.log(obj[key])
}
fn(person, 'name') // zs
fn(person, 'age') // 18
高阶用法
interface Data {
name: string
age: number
sex: string
}
type Options<T extends object> = {
[key in keyof T]?: T[key]
}
type B = Options<Data>
泛型类
声明方法跟函数类似名称后面定义<类型>
class Sub<T> {
attr: T[] = []
add(a: T): T[] {
return [a]
}
}
let s = new Sub<number>()
s.attr = [1, 2, 3]
console.log(s.add(123)) // [123]
let str = new Sub<string>()
str.attr = ['1', '2', '3']
console.log(str.add('123')) // ['123']