TypeScript实例讲解(九)

241 阅读2分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

接上一篇:TypeScript实例讲解(八)

本篇内容:TypeScript 泛型。

泛型

TypeScript 泛型的概念理解起来比较困难,但是泛型的应用场景非常广泛,在开源 TS 项目源码或者第三方库等很多地方都能看到它。

泛型是泛指的类型,也可以理解为一种类型,只是不同于 stringnumber 等具体的类型,它是一种抽象的类型,我们不能直接定义一个变量的类型为泛型。

函数泛型

// 例 1 
function splicing<T>(first: T, second: T) {
    return `${first}${second}`
}
splicing<number>(1, 2)   // 12

泛型的名称一般简写为T。例1中的 splicing() 函数使用了泛型,在函数定义的时候并不知道参数是什么类型,在调用时才能知道,但是函数会接收一个泛指的类型 T,调用的时候再指定具体的类型。

// 例 2
function splicing<T, U>(first: T, second: U) {
    return `${first}${second}`
}
splicing<number, string>(1, '2')   // 12

在同一个函数中我们不仅可以定义一个类型,还可以定义多个类型的泛型(如例2)。另外不仅能在函数参数上用泛型作为类型,而且还可以用在函数返回值上。

类中的泛型

// 例 3
class DataManager<T> {
    constructor(private data: T[]) {}
    getItem(index: number): T {
        return this.data[index];
    }
}
// 实例化 1
const data1 = new DataManager<number>([1, 2]);
data1.getItem(0)    // 1
// 实例化 2
const data2 = new DataManager<string>(['大熊猫', '小熊猫']);
data2.getItem(0)    // 大熊猫

例3中的类 DataManager 也定义了一个泛型 T,算是类中最基础的泛型了。

再来看一个更深入的例子,我们希望泛型 T 对应的类型都有一个 name 属性。

// 例 4
interface Item {
    name: string
}
class DataManager<T extends Item> {
    constructor(private data: T[]) {}
    getItem(index: number): string {
        return this.data[index].name;
    }
}
const data = new DataManager([
    { name: 'bear'}
]);

为了满足我们的要求,例4先定义了一个接口 Item,然后让泛型 T 去继承 Item。这样泛型就必须包含 Item 里面的 name 属性了。

// 例 5
class DataManager<T extends number | string> {
    // todo
}

之前的例子中泛型可以指定成任意类型,例5中的泛型 T 就只能为 numberstring 类型。

从上面的几个例子可以看出泛型解决了代码灵活性等问题。泛型和我们平时使用的函数很像,如果将两者进行横向对比,会容易理解一些。

本篇完!如果文章对你有一点点帮助,请记得点个赞哦。