TypeScript 泛型(Generic)
1. 解释
function generic<T>() {}
interface Generic<T> {}
class Generic<T> {}
2. 初识泛型
之所以使用泛型,是因为它帮助我们为不同类型的输入,复用相同的代码。
function identity(arg: number): number {
return arg
}
如果传入的是 string 类型:
function identity(arg: string): string {
return arg
}
通过泛型,可以把两个函数统一起来:
function identity<T>(arg: T): T {
return arg
}
需要注意的是,泛型函数的返回值类型是根据你的业务需求决定,并非一定要返回泛型类型 T:
function identity<T>(arg: T): string {
return String(arg)
}
代码解释: 入参的类型是未知的,但是通过 String 转换,返回字符串类型。
3. 多个类型参数
泛型函数可以定义多个类型参数:
function extend<T, U>(first: T, second: U): T & U {
for(const key in second) {
(first as T & U)[key] = second[key] as any
}
return first as T & U
}
4. 泛型参数默认类型
function min<T = number>(arr:T[]): T{
let min = arr[]
arr.forEach((value)=>{
if(value < min) {
min = value
}
})
return min
}
console.log(min([, , 8n])) // 6
5. 泛型类型与泛型接口
先来回顾下之前章节介绍的函数类型:
const add: (x: number, y: number) => string = function(x: number, y: number): string {
return (x + y).toString()
}
等号左侧的 (x: number, y: number) => string
为函数类型。
再看下泛型类型:
function identity<T>(arg: T): T {
return arg
}
let myIdentity: <T>(arg: T) => T = identity
同样的等号左侧的 <T>(arg: T) => T
即为泛型类型,它还有另一种带有调用签名的对象字面量书写方式:{ <T>(arg: T): T }
:
function identity<T>(arg: T): T {
return arg
}
let myIdentity: { <T>(arg: T): T } = identity
interface GenericIdentityFn {
<T>(arg: T): T
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn = identity
进一步,把泛型参数当作整个接口的一个参数,我们可以把泛型参数提前到接口名上。这样我们就能清楚的知道使用的具体是哪个泛型类型:
interface GenericIdentityFn<T> {
(arg: T): T
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn<number> = identity
6. 泛型类
class MinClass {
public list: number[] = []
add(num: number) {
this.list.push(num)
}
min(): number {
let minNum = this.list[]
for (let i = ; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
// 类名后加上 <T>
class MinClass<T> {
public list: T[] = []
add(num: T) {
this.list.push(num)
}
min(): T {
let minNum = this.list[]
for (let i = ; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
let m = new MinClass<string>()
m.add('hello')
m.add('world')
m.add('generic')
console.log(m.min()) // generic
代码解释:
7. 泛型约束
语法:通过 extends
关键字来实现泛型约束。
如果我们很明确传入的泛型参数是什么类型,或者明确想要操作的某类型的值具有什么属性,那么就需要对泛型进行约束。通过两个例子来说明:
interface User {
username: string
}
function info<T extends User>(user: T): string {
return 'imooc ' + user.username
}
下面再看另外一个例子:
type Args = number | string
class MinClass<T extends Args> {}
const m = new MinClass<boolean>() // Error, 必须是 number | string 类型
代码解释:
第 3 行,约束了泛型参数 T 继承自类型 Args,而类型 Args 是一个由 number 和 string 组成的联合类型。
第 5 行,泛型参数只能是 number 和 string 中的一种,传入 boolean 类型是错误的。
8. 多重类型泛型约束
通过 <T extends Interface1 & Interface2>
这种语法来实现多重类型的泛型约束:
interface Sentence {
title: string,
content: string
}
interface Music {
url: string
}
class Classic<T extends Sentence & Music> {
private prop: T
constructor(arg: T) {
this.prop = arg
}
info() {
return {
url: this.prop.url,
title: this.prop.title,
content: this.prop.content
}
}
}
代码解释:
第 10 行,约束了泛型参数 T
需继承自交叉类型(后续有单节介绍) Sentence & Music
,这样就能访问两个接口类型的参数。