20. TypeScript 类中泛型(难点)
上节我们学习了在函数(方法)中使用泛型的基本语法,这节在看看类中泛型的使用方法。
编写一个基本类
为了下面的教学演示,所以我先编写一个基本的类SelectGirl
,在类的构造函数中(constructor)需要传递一组女孩的名称,然后再通过下边展现女孩的名称,代码如下:
class SelectGirl { constructor(private girls: string[]) {} getGirl(index: number): string { return this.girls[index]; } } const selectGirl = new SelectGirl(["小红", "小丽", "小影"]); console.log(selectGirl.getGirl(1));
写完后,我们可以在终端中使用ts-node Demo.ts
进行预览,可以看到控制台中输出了小丽
的名字。学到现在你写这样的一个类应该是非常容易的了。
现在问题来了,比如现在更好的保护小姐姐,这些小姐姐使用编号啦,那我们程序要如何修改。需要写成下面的样子,这时候我们代码看起来就没有那么优雅了,在 TypeScript 中,编写复杂代码的时候,会经常使用泛型。
class SelectGirl { constructor(private girls: string[] | number[]) {} getGirl(index: number): string | number { return this.girls[index]; } }
初始类的泛型
这时候我们要用泛型重构代码,要如何作那?有了上节课的基础,应该很好理解,就是用<>
编写,我们把代码修改成了这个样子。
class SelectGirl<T> { constructor(private girls: T[]) {} getGirl(index: number): T { return this.girls[index]; } } const selectGirl = new SelectGirl(["小红", "小丽", "小影"]); console.log(selectGirl.getGirl(1));
这时候代码并不报错,也使用了泛型,但是在实例化对象的时候,TypeScript 是通过类型推断出来的。上节已经介绍,这种方法并不好,所以还是需要在实例化对象的时候,对泛型的值进行确定,比如是string
类型,就这样写。
const selectGirl = new SelectGirl() < string > ["小红", "小丽", "小影"];
这就是类里边最基础的泛型使用了,如果你还不理解,请现在敲出上面的例子进行练习,不要继续学习了。
泛型中的继承
现在需求又变了,要求返回是一个对象中的name
,也就是下面的代码要改成这个样子。
return this.girls[index].name;
现在的代码一定是报错的,但是这时候还要求我们这么做,意思就是说传递过来的值必须是一个对象类型的,里边还要有name
属性。这时候就要用到继承了,我用接口的方式来实现。写一个Girl
的接口,每个接口里都要有 name 属性。代码如下:
interface Girl { name: string; }
有了接口后用extends
关键字实现泛型继承,代码如下:
class SelectGirl<T extends Girl> { ... }
这句代码的意思是泛型里必须有一个name
属性,因为它继承了Girl
接口。
现在程序还是报错的,因为我们getGirl
方法的返回类型还不对,这时候应该是一个string
类型才对,所以代码应该改为下面的样子:
interface Girl { name: string; } class SelectGirl<T extends Girl> { constructor(private girls: T[]) {} getGirl(index: number): string { return this.girls[index].name; } } const selectGirl = new SelectGirl([ { name: "小红" }, { name: "小丽" }, { name: "小影" }, ]); console.log(selectGirl.getGirl(1));
我们回过头来看一下这段代码的意思,就是我们在SelectGirl
类中使用了泛型,意思是我不知道我以后要用什么类型,但是我有一个约束条件,这个类型,必须要有一个name
属性。这个在工作中经常使用,所以必须要好好理解这的知识。 初学泛型肯定会很难理解,我当时看书也是看的一脸懵,经过反复的实验和看别人的源代码,才对泛型有了比较深的理解。
泛型约束
现在的泛型可以是任意类型,可以是对象、字符串、布尔、数字都是可以的。但你现在要求这个泛型必须是string
或者number
类型。我们还是拿上面的例子,不过把代码改为最初的样子。
class SelectGirl<T> { constructor(private girls: T[]) {} getGirl(index: number): T { return this.girls[index]; } } const selectGirl = new SelectGirl<string>(["小红", "小丽", "小影"]); console.log(selectGirl.getGirl(1));
然后进行约束,这时候还是可以使用关键字extends
来进行约束,把代码改成下面的样子。
class SelectGirl<T extends number | string> { //..... }
作为教学泛型讲这些就可以了,但是在实际工作中,泛型的应用更广泛和复杂,这些需要在实际项目中不断精进和加深理解。
码云笔记 » 20. TypeScript 类中泛型(难点)