软件工程的主要目的是构建不仅仅明确和一致的API,还要让你的代码具有很强的可重用性
比如我们可以通过函数来封装一些API,通过传入不同的函数参数,让函数帮助我们完成不同的操作
所以对于函数而言,其参数的类型往往是不固定的,此时我们就可以使用泛型
所谓泛型,其本质就是将参数类型变量化的过程
// Type 就是一个类型变量 其具体值取决于外部传入的值
// 参数变量命名 一般是大驼峰变量 如Type 或大写的字母 如T
// 泛型和函数参数一样 是可以设置对应的默认值的
// 如果泛型没有设置默认值,且没有传参的时候,默认值会被推导为unknown
function foo<Type = string>(arg: Type): Type {
return arg
}
const res = foo<string>('Klaus') // typeof res -> string
// 默认情况下,ts可以根据我们传入的值的类型推测出正确的类型参数的类型
const num1 = foo(111) // typeof num1 -> 111
let num2 = foo(222) // typeof num2 -> number
常见的泛型参数名
| 名称 | 说明 |
|---|---|
| T | Type的缩写,类型 |
| K, V | key和value的缩写,键值对 |
| E | Element的缩写,元素 |
| O | Object的缩写,对象 |
| R | ReturnType的缩写,函数返回值 |
示例
泛型接口
// 泛型参数不仅仅可以定义在整个接口上(全局泛型)
// 也可以定义在泛型成员上(局部泛型)
interface IFoo<T> {
name: T
}
// 对于接口泛型 --- ts并不能自动推导,所以需要手动指定
const foo: IFoo<string> = {
name: 'Klaus'
}
泛型类
class Foo<T> {
name: T
constructor(name: T) {
this.name = name
}
}
const foo = new Foo('Klaus')
console.log(foo.name) // typeof foo.name -> string
泛型约束
有时候我们希望传入的类型有某些共性,但是这些共性可能不是在同一种类型中
比如string和array都是有length的,或者某些对象也是会有length属性
如果我们希望一个类型可以同时满足上述所有条件,此时就可以使用泛型约束
function foo(arg: { length: number }) {
return arg
}
/*
此时res1和res2会丢失返回的类型
此时res1 和 res2 的类型 都是 { length: number }
*/
const res1 = foo('123')
const res2 = foo([1, 2, 3])
function foo<T extends { length: numbr }>(arg: T) {
return arg
}
// 因为原本的类型使用泛型变量T保存了下来,且使用extends关键字进行了约束
// 所以此时类型即满足了约束,且不会丢失原本的类型
const res1 = foo('123') // typeof res1 -> '123'
const res2 = foo([1, 2, 3]) // typeof res2 -> [1, 2, 3]
function getV<O, K extends keyof O>(obj: O, key: K) {
return obj[key]
}
const user = {
name: 'Klaus',
age: 23
}
const name = getV(user, 'name')
export {}