什么是泛型?
我之前学Java的时候,就接触过泛型。其实就是不显式指定传入参数的类型,这样就可以传入不同类型的参数复用代码。
同样的,在TypeScript中,T作为类型的占位符。
类中使用:这样在使用类时才传入具体的类型,collection中就可以存不同类型的数据。
class ArrayOfAnything<T> {
constructor(public collection: T[]) {}
get(index: number): T {
return this.collection[index];
}
}
函数中使用:可以不用any实现传入不同类型的参数。
function printAnything<T>(arr: T[]): void {
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
}
泛型约束
通过继承接口来约束泛型内要实现某些属性或方法
function printHousesOrCars<T extends Printable>(arr: T[]): void {
for (let i = 0; i < arr.length; i++) {
arr[i].print();
}
}
泛型实践
泛型一般是在需要处理多种类型时设置。相同的处理逻辑可以直接在类中实现,不同的逻辑可以写为抽象方法,由继承的子类实现。
实战需求:实现一个读取csv文件的类
解决方案
csv中的数据可能是不同的,我们读取处理后想要得到的数据需求可能是变化的。那么对于处理后得到的数据类型,我们定义为泛型。将针对不同类型会有变化的读取处理数据的部分定义为抽象方法。
import fs from 'fs';
export abstract class CsvFileReader<T> {
data: T[] = [];
constructor(public filename: string) {}
abstract mapRow(row: string[]): T;
read(): void {
this.data = fs
.readFileSync(this.filename, {
encoding: 'utf-8'
})
.split('\n')
.map(
(row: string): string[] => {
return row.split(',');
}
)
.map(this.mapRow);
}
}
这样在使用中只要给出读取数据的类型,和处理数据的抽象方法的实现就可以了。
type MatchData = [Date, string, string, number, number, MatchResult, string];
export class MatchReader extends CsvFileReader<MatchData> {
mapRow(row: string[]): MatchData {
return [
dateStringToDate(row[0]),
row[1],
row[2],
parseInt(row[3]),
parseInt(row[4]),
row[5] as MatchResult,
row[6]
];
}
}
这样之后增加新的需求也可以复用读取数据的代码,只要提供新的数据类型和抽象方法实现。