TypeScript中的泛型:聚在一起

大家好! TestMace团队发布了来自Web开发领域的另一篇文章翻译。 这次是初学者! 阅读愉快。


打破关于<T>语法的神秘面纱和误解,并最终与之成为朋友



可能只有Java或其他强类型语言的经验丰富的开发人员在TypeScript中看到泛型时才大吃一惊。 它的语法从根本上不同于我们过去在JavaScript中看到的所有语法,因此要立即猜测它的作用并不容易。


我想向您展示,实际上,所有事情都比看起来简单得多。 我将证明,如果您能够在JavaScript中实现带有参数的函数,那么您可以使用泛型而无需任何额外的工作。 走吧


TypeScript中的泛型


TypeScript文档提供以下定义:“泛型是创建不仅可以使用一种数据,而且可以使用多种数据类型的组件的能力。”


哇! 因此,主要思想是泛型允许我们创建一些可重用的组件,这些组件可处理传输给它们的各种类型的数据。 但这怎么可能呢? 这就是我的想法。


泛型和类型彼此相关,例如函数值和参数。 这是一种告诉组件(函数,类或接口)调用它们时使用哪种类型的方法,就像在调用过程中我们告诉函数将哪些值用作参数一样。


最好以相同功能的泛型为例来理解这一点。 相同的函数是返回传递给它的参数值的函数。 在JavaScript中,它将如下所示:


identity.js
 function identity (value) { return value; } console.log(identity(1)) // 1 

让我们使用数字:


身份
 function identity (value: Number) : Number { return value; } console.log(identity(1)) // 1 

好吧,我们在相同函数的定义中添加了一个类型,但是我们希望它更加灵活并且可以用于任何类型的值,而不仅仅是数字。 这就是泛型的用途。 它们允许函数在输入处获取任何类型的数据的值,并根据它们转换函数本身。


genericIdentity.ts
 function identity <T>(value: T) : T { return value; } console.log(identity<Number>(1)) // 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()) // 1 const myStringClass = new IdentityClass<string>("Hello!") console.log(myStringClass.getIdentity()) // Hello! 

如果无法立即理解该代码,请尝试从右至右跟踪type值直至函数调用。 步骤如下:


  1. 创建一个IdentityClass类的新实例,并将Number类型和值1传递给它。
  2. 在该类中, T的值T分配为Number类型。
  3. IdentityClass实现GenericInterface<T> ,并且我们知道TNumber ,并且这样的记录等效于GenericInterface<Number>条目。
  4. GenericInterface通用U变为Number 。 在此示例中,我有意使用了不同的变量名来表明类型值在链上,而变量名没有任何意义。

实际用例:超越原始类型


在以上所有代码插入中,均使用了Numberstring类的原始类型。 例如,这是最多的, 但实际上,您不太可能将泛型用于原始类型。 当使用形成继承树的任意类型或类时,泛型将非常有用。


考虑继承的经典示例。 假设我们有一个Car类,这是TruckVespa类的基础。 我们编写了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值传递给函数:)


如果您想了解有关泛型的更多信息,请在下面附加几个链接。


阅读内容


Source: https://habr.com/ru/post/zh-CN455473/


All Articles