外观
TS 泛型
作者:guo-zi-xin
更新于:9 个月前
字数统计:1.4k 字
阅读时长:5 分钟
定义
在 TypeScript 中,泛型(Generics)是一种强大的类型工具,它允许我们编写可重用、灵活和类型安全的代码。泛型允许我们在定义函数、类或接口时使用类型参数,这些类型参数在使用时可以被动态地指定具体的类型。
特点:
不是事先定义好类型,而是在使用的时候再指定具体类型
1. 数组泛型(Array Generic)
我们在指定一个数组的类型时,可以使用 类型 + 中括号 表示法来定义,例如: number[]
来作为数字数组类型,字符串数组为string[]
, 除此之外,我们也可以使用泛型Array<elemType>
来定义一个数组:
typescript
const arr: Array<number> = [1,2,3,4,5]
const strArr: Array<string> = ['a','b','c']
const anyArr: Array<any> = [1, 'b', [3,4], {text: 'c'}, null]
2. 函数泛型
函数泛型允许我们编写可以适用多种类型的函数, 提高代码的灵活性和可复用性。
简单的例子 我们可以实现一个函数createArray
, 它可以创建一个指定长度的数组, 并且将每一项填充一个默认值:
typescript
const createArray = (length: number, value: any): any[] => {
let results: any[] = []
for (let key = 0, key <= length; key++) {
results[key] = value
}
return results
}
上述示例中, 由于不确定填充值value
的类型, 使用了any
类型来指定类型, 并且返回值也是any
类型的数组, 虽然编译不会报错, 但我们无法得到确定的返回值的类型; 这个时候,泛型就派上用场了:
typescript
function createArray<T>(length:number, value: T): Array<T> {
let results: Array<T> = [];
for (let key = 0, key < length; key++) {
results[key] = value
}
return results
}
// 箭头函数
const createArray = <T>(length: number, value: T): Array<T> => {
let results: Array<T> = [];
for (let key = 0, key < length; key++) {
results[key] = value
}
return results
}
createArray<string>(3, 'x') // 返回 ['x', 'x', 'x']
我们在函数名后使用了<T>
, 其中T
表示任意类型, 后面的value
参数和函数返回值就可以直接使用Array<T>
了。 在调用时,我们传入string
类型, 表示指定它的类型为字符串, 当然也可以不指定类型, 由类型推断自动推断出来:
typescript
createArray(3, 'x') // 返回 ['x', 'x', 'x'], 通过‘x’的类型自动·推断出泛型的类型
多个类型参数 定义泛型的时候,可以一次定义多个类型参数:
typescript
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap<number,string>([7, 'seven']); // ['seven', 7]
上述示例中, 我们实现了一个swap
函数, 用于交换输入中的元组。
泛型约束 在函数内部使用泛型参数时, 由于不知道参数的具体类型, 所以不能够随意使用它的属性与方法:
typescript
const loggingIdentity: <T>(arg: T) =>T = <T>(arg:T): T => {
console.log(arg.length);
return arg
}
loggingIdentity(0)
这个时候, 我们可以在函数参数类型上加上一些限制, 只能让它穿入那些包含length
属性的变量, 这个限制操作就是泛型约束
typescript
interface Lengthwise {
length: number;
}
const loggingIdentity: <T extends Lengthwise>(arg: T) =>T = <T extends Lengthwise>(arg:T): T => {
console.log(arg.length);
return arg
}
const arr = [1,2,3,4,5]
const num = 5
loggingIdentity(arr)
loggingIdentity(num) // ts编译错误
多个泛型类型也可以互相约束:
typescript
const copyFileds = <T extends U, U>(target: T, source: U): T => {
for (const key in source) {
target[key] = (<T>source)[key];
}
return target;
}
const obj1 = { name: '张三' }
const obj2 = { name: '张三', sex: '男' }
const obj3 = { age: 18 }
copyFileds(obj2, obj1) //正常编译
copyFileds(obj3, obj1) //ts编译报错,obj3缺少来自obj2的 name 属性
上述示例中,我们使用T
、U
两个类型字段, 并且指定T
继承自U
, 这样就保证了T
不会出现U
不存在的字段
typescript
const getData = <T extends object, K extends keyof T>(obj: T, key: K):T[K] => {
return obj[key]
};
const defaultObj = {name: '张三', age: 18};
getData(defaultObj, 'name')
上述示例中, 我们实现了一个getData
的函数, 它的功能是返回目标对象的目标key的值, 关于类型, 我们约定了K的取值范围只能来自终于T的key值, 使用其它的类型会报错
3. 接口泛型
接口泛型允许我们创建可适用于不同类型的接口定义。例如:
- 基本用法
typescript
interface IResponse<T, U> {
str :T,
num: U
}
const newName: IResponse<string, number> = {str: '11', num: 22}
上述示例中, 我们定义了一个IResponse
的接口, 它接受两个类型参数T
和U
, 通过指定类型参数为number
和string
,我们创建了一个具体的newName
对象, 它的str
类型是string
,num
的类型是number
。
定义函数的形状
类型确定情况
typescriptinterface ISearchFunc { (source: string, subString: string): boolean; } const createArray:ISearchFunc createArray = (source: string, subString: string) =>{ return source.search(subString) !== -1; }
泛型定义函数
typescriptinterface ICreateArray { <T>(length: number, value: T): T[] } const createArray: ICreateArray = <T>(length: number, value: T): Array<T> => { const result:T[] = [] for(let k = 1; k<= length; k++) { result[k] = value } return result } createArray(3, 'y')
也可以提前把泛型定义到接口上
typescriptinterface ICreateArray<T> { (length: number, value: T): T[] } const createArray: ICreateArray<unknown> = <T>(length: number, value: T): Array<T> => { const result:T[] = [] for(let k = 1; k<= length; k++) { result[k] = value } return result } createArray(3, 'y')
⚠️注意:此时定义接口类型时候, 需要传入泛型的类型
4. 类泛型
与接口类型相似, 泛型也可以用于类的类型定义中
typescript
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };