经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » TypeScript » 查看文章
TypeScript泛型的使用详细介绍
来源:jb51  时间:2022/9/20 9:28:54  对本文有异议

情景再现

这里针对一种情况,也是非常常见的一种情况:那就是

  1. function identity(arg: number): number {
  2. return arg;
  3. }

就是我接收一个number类型的参数,同时也返回一个number,那如果现在我想要接收一个string类型,同时也返回一个string,那么我就要再写一个函数像这样:

  1. function identity2(arg: string): string{
  2. return arg;
  3. }

那如果我现在想要void类型…??????

可能大家会想,那全部都变成any不就行了?像下面这样

  1. function identity(arg: any): any {
  2. return arg;
  3. }

使用any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。 如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。

那这样不就与我们一开始的设想不一致了吗?我传入Number,返回string,也不会报错呀!??

因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了类型变量,它是一种特殊的变量,只用于表示类型而不是值。

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }

我们给identity添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。

??‍??我们把这个版本的identity函数叫做泛型,因为它可以适用于多个类型。 不同于使用any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。

使用泛型

第一种是,传入所有的参数,包含类型参数

第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. let output = identity<string>("myString");
  5. let output2 = identity("myString2");
  6. console.log(output);
  7. console.log(output2);

注意我们没必要使用尖括号(<>)来明确地传入类型;编译器可以查看myString的值,然后把T设置为它的类型。 类型推论帮助我们保持代码精简和高可读性。如果编译器不能够自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的情况下,这是可能出现的。

泛型类型

我们研究一下函数本身的类型,以及如何创建泛型接口。

来看看泛型类型不同的展现方式:

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. let myFunction: <T>(arg:T) => T = identity;

我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. let myFunction: <T>(arg:T) => T = identity;
  5. let myIdentity: <U>(arg: U) => U = identity;

我们还可以使用带有调用签名的对象字面量来定义泛型函数:

  1. function identity<T>(arg: T): T {
  2. return arg;
  3. }
  4. let myIdentity: {<T>(arg: T): T} = identity;

是不是花了眼哈哈哈哈哈

泛型接口

还是以上面的为例子噢

  1. interface GenericIdentityFn {
  2. <T>(arg: T): T;
  3. }
  4. function identity<T>(arg: T): T {
  5. return arg;
  6. }
  7. let myIdentity: GenericIdentityFn = identity;

可不可以详细一点

一个相似的例子,我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型(比如:Dictionary< string>而不只是Dictionary)。 这样接口里的其它成员也能知道这个参数的类型了。

  1. interface GenericIdentityFn<T> {
  2. (arg: T): T;
  3. }
  4. function identity<T>(arg: T): T {
  5. return arg;
  6. }
  7. let myIdentity: GenericIdentityFn<number> = identity;

泛型类

我等这个泛型类等了好久好久??????

泛型类看上去与泛型接口差不多。 泛型类使用(<>)括起泛型类型,跟在类名后面。

  1. // 泛型类
  2. class GenericNumber<T> {
  3. zeroValue: T | undefined;
  4. add: ((x: T, y: T) => T) | undefined;
  5. }
  6. let myGenericNumber = new GenericNumber<number>();
  7. myGenericNumber.zeroValue = 0;
  8. function myAdd(x:number, y:number) {
  9. return x + y;
  10. };
  11. myGenericNumber.add = function(x, y) {
  12. return x + y;
  13. };
  14. console.log(myGenericNumber.add(1,2));

换string玩玩

  1. // 泛型类
  2. class GenericNumber<T> {
  3. zeroValue: T | undefined;
  4. add: ((x: T, y: T) => T) | undefined;
  5. }
  6. let stringNumeric = new GenericNumber<string>();
  7. stringNumeric.zeroValue = "hi,";
  8. stringNumeric.add = function(x, y) { return x + y; };
  9. console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));

也不是全能的

与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。

注意点??????类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。

泛型约束

到底有没有长度啊,救命

当我们使用泛型的时候,有这种情况:

我想要打印出传过来的参数的长度为多少

  1. function loggingIdentity<T>(arg: T): T {
  2. console.log(arg.length); // Error: T doesn't have .length
  3. return arg;
  4. }

这里会扯到一个问题,首先,你传过来的这个玩意儿,它本身有长度吗??

首先,什么样的类型会有长度,毫无疑问,数组嘛

那我如果传入的不是数组,那就铁定报错,就像上面那样,正确的写法大家也都懂:

  1. function loggingIdentity<T>(arg: T[]): T[] {
  2. console.log(arg.length); // Array has a .length, so no more error
  3. return arg;
  4. }

相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。

  1. interface Lengthwise {
  2. length: number;
  3. }
  4. function loggingIdentity<T extends Lengthwise>(arg: T): T {
  5. console.log(arg.length); // Now we know it has a .length property, so no more error
  6. return arg;
  7. }

现在传入一个数字试试

传入数组:

我们需要传入符合约束类型的值,必须包含必须的属性:

在泛型里使用类类型[]

在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。

跟在Java中的很像——工厂模式,很是高级

  1. function create<T>(c: {new(): T; }): T {
  2. return new c();
  3. }

高级案例

应用场景:传入这个类,自动创建该类并且返回相应的属性。

  1. class BeeKeeper {
  2. hasMask: boolean = false;
  3. }
  4. class ZooKeeper {
  5. nametag:string = "ZooKeeper.nametag";
  6. }
  7. class Animal {
  8. numLegs: number = 100;
  9. }
  10. class Bee extends Animal {
  11. keeper: BeeKeeper = new BeeKeeper();
  12. }
  13. class Lion extends Animal {
  14. keeper: ZooKeeper = new ZooKeeper();
  15. }
  16. function createInstance<A extends Animal>(c: new () => A): A {
  17. return new c();
  18. }
  19. console.log(createInstance(Lion).keeper.nametag);
  20. console.log(createInstance(Bee).keeper.hasMask);

到此这篇关于TypeScript泛型的使用详细介绍的文章就介绍到这了,更多相关TypeScript泛型内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号