大家好! TestMace团队发布了来自Web开发领域的另一篇文章翻译。 这次是初学者! 阅读愉快。
打破关于<T>
语法的神秘面纱和误解,并最终与之成为朋友

可能只有Java或其他强类型语言的经验丰富的开发人员在TypeScript中看到泛型时才大吃一惊。 它的语法从根本上不同于我们过去在JavaScript中看到的所有语法,因此要立即猜测它的作用并不容易。
我想向您展示,实际上,所有事情都比看起来简单得多。 我将证明,如果您能够在JavaScript中实现带有参数的函数,那么您可以使用泛型而无需任何额外的工作。 走吧
TypeScript中的泛型
TypeScript文档提供以下定义:“泛型是创建不仅可以使用一种数据,而且可以使用多种数据类型的组件的能力。”
哇! 因此,主要思想是泛型允许我们创建一些可重用的组件,这些组件可处理传输给它们的各种类型的数据。 但这怎么可能呢? 这就是我的想法。
泛型和类型彼此相关,例如函数值和参数。 这是一种告诉组件(函数,类或接口)调用它们时使用哪种类型的方法,就像在调用过程中我们告诉函数将哪些值用作参数一样。
最好以相同功能的泛型为例来理解这一点。 相同的函数是返回传递给它的参数值的函数。 在JavaScript中,它将如下所示:
identity.js function identity (value) { return value; } console.log(identity(1))
让我们使用数字:
身份 function identity (value: Number) : Number { return value; } console.log(identity(1))
好吧,我们在相同函数的定义中添加了一个类型,但是我们希望它更加灵活并且可以用于任何类型的值,而不仅仅是数字。 这就是泛型的用途。 它们允许函数在输入处获取任何类型的数据的值,并根据它们转换函数本身。
genericIdentity.ts function identity <T>(value: T) : T { return value; } console.log(identity<Number>(1))
哦,那奇怪的<T>
语法! 别慌了 我们只是传递要用于特定函数调用的类型。

看上面的图片。 当您调用identity<Number>(1)
, Number
类型的参数与1相同。它随处都替换为T
函数可以采用几种类型,就像采用几种参数一样。

查看函数调用。 现在,通用语法不应吓scar您。 T
和 U
只是您自己分配的变量的名称。 调用函数时,将指示该函数将使用的类型 。
理解泛型概念的另一种形式是,它们根据指定的数据类型来转换功能。 下面的动画显示了更改类型时函数记录和返回的结果如何变化。

如您所见,该函数接受任何类型,这使您可以创建各种类型的可重用组件,如文档中所述。
请特别注意上面动画中对console.log的第二次调用-类型没有传递给它。 在这种情况下,TypeScript将尝试从传输的数据中计算类型。
通用类和接口
您已经知道泛型只是将类型传递给组件的一种方法。 您刚刚看到了它们如何与函数一起使用,并且我得到了一个好消息:它们与类和接口以相同的方式工作。 在这种情况下,类型指示位于接口或类的名称之后。
看一个例子,尝试自己弄清楚。 我希望你成功了。
genericClass.ts interface GenericInterface<U> { value: U getIdentity: () => U } class IdentityClass<T> implements GenericInterface<T> { value: T constructor(value: T) { this.value = value } getIdentity () : T { return this.value } } const myNumberClass = new IdentityClass<Number>(1) console.log(myNumberClass.getIdentity())
如果无法立即理解该代码,请尝试从右至右跟踪type
值直至函数调用。 步骤如下:
- 创建一个
IdentityClass
类的新实例,并将Number
类型和值1
传递给它。 - 在该类中,
T
的值T
分配为Number
类型。 IdentityClass
实现GenericInterface<T>
,并且我们知道T
是Number
,并且这样的记录等效于GenericInterface<Number>
条目。- 在
GenericInterface
通用U
变为Number
。 在此示例中,我有意使用了不同的变量名来表明类型值在链上,而变量名没有任何意义。
实际用例:超越原始类型
在以上所有代码插入中,均使用了Number
和string
类的原始类型。 例如,这是最多的, 但实际上,您不太可能将泛型用于原始类型。 当使用形成继承树的任意类型或类时,泛型将非常有用。
考虑继承的经典示例。 假设我们有一个Car
类,这是Truck
和Vespa
类的基础。 我们编写了washCar实用程序函数,该函数接受Car
的通用实例并将其返回。
汽车 class Car { label: string = 'Generic Car' numWheels: Number = 4 horn() { return "beep beep!" } } class Truck extends Car { label = 'Truck' numWheels = 18 } class Vespa extends Car { label = 'Vespa' numWheels = 2 } function washCar <T extends Car> (car: T) : T { console.log(`Received a ${car.label} in the car wash.`) console.log(`Cleaning all ${car.numWheels} tires.`) console.log('Beeping horn -', car.horn()) console.log('Returning your car now') return car } const myVespa = new Vespa() washCar<Vespa>(myVespa) const myTruck = new Truck() washCar<Truck>(myTruck)
通过washCar
函数T extends Car
,我们指出了可以在此函数内部使用的函数和属性。 Generic还允许您返回指定类型的数据,而不是通常的Car
。
该代码的结果将是:
Received a Vespa in the car wash. Cleaning all 2 tires. Beeping horn - beep beep! Returning your car now Received a Truck in the car wash. Cleaning all 18 tires. Beeping horn - beep beep! Returning your car now
总结一下
希望我能帮助您处理仿制药。 记住,您要做的只是将type
值传递给函数:)
如果您想了解有关泛型的更多信息,请在下面附加几个链接。
阅读内容 :