初学者使用TypeScript,第5部分:泛型

我们的TypeScript for Beginners系列的第二篇教程重点介绍TypeScript中可用的基本数据类型 。 通过TypeScript中的类型检查,我们可以确保代码中的变量只能分配有特定类型的值。 这样,在编写代码时我们可以避免很多错误,因为IDE可以告诉我们何时以不支持的类型执行操作。 这使类型检查成为TypeScript的最佳功能之一。

在本教程中,我们将重点介绍该语言的另一个重要功能-泛型。 使用泛型,TypeScript使您可以编写可对多种数据类型起作用的代码,而不仅限于一种。 您将详细了解对泛型的需求,以及它与仅使用TypeScript中可用的any数据类型相比有何好处。

对泛型的需求

如果不熟悉泛型,您可能想知道为什么我们根本需要它们。 在本节中,我将为您回答这个问题。 让我们从编写一个函数开始,该函数将从数字数组中返回一个随机元素。

function randomIntElem(theArray: number[]): number {
    let randomIndex = Math.floor(Math.random()*theArray.length);
    return theArray[randomIndex];
}

let positions: number[] = [103, 458, 472, 458];
let randomPosition: number = randomIntElem(positions);

我们刚刚定义的randomElem函数将数字数组作为唯一参数。 函数的返回类型也已指定为数字。 我们使用Math.random()函数返回0到1之间的浮点随机数。将其与给定数组的长度相乘,并在结果上调用Math.floor()会为我们提供一个随机索引。 获得随机索引后,我们将在该特定索引处返回元素。

稍后,假设您需要从字符串数组中获取随机字符串元素。 此时,您可以决定创建另一个专门针对字符串的函数。

function randomStrElem(theArray: string[]): string {
    let randomIndex = Math.floor(Math.random()*theArray.length);
    return theArray[randomIndex];
}

let colors: string[] = ['violet', 'indigo', 'blue', 'green'];
let randomColor: string = randomStrElem(colors);

如果需要从定义的接口数组中选择随机元素怎么办? 每当您想从一组不同类型的对象中获取随机元素时,创建一个新函数都是不可行的。

解决此问题的一种方法是将要传递给函数的数组参数的类型设置为any[] 。 这样,您只可以编写一次函数,它将与所有类型的数组一起使用。

function randomElem(theArray: any[]): any {
    let randomIndex = Math.floor(Math.random()*theArray.length);
    return theArray[randomIndex];
}

let positions = [103, 458, 472, 458];
let randomPosition = randomElem(positions);

let colors = ['violet', 'indigo', 'blue', 'green'];
let randomColor = randomElem(colors);

如您所见,我们可以使用上面的函数来获取随机位置和随机颜色。 该解决方案的一个主要问题是,您将丢失有关所返回的值类型的信息。

之前,我们确定randomPosition将是一个数字,而randomColor将是一个字符串。 这有助于我们相应地使用这些值。 现在,我们所知道的是,返回的元素可以是任何类型。 在上面的代码中,我们可以将randomColor的类型指定为number并且仍然不会出现任何错误。

// This code will compile without an error.
let colors: string[] = ['violet', 'indigo', 'blue', 'green'];
let randomColor: number = randomElem(colors);

避免代码重复而又保留类型信息的更好解决方案是使用泛型。 这是一个泛型函数,可从数组中返回随机元素。

function randomElem<T>(theArray: T[]): T {
    let randomIndex = Math.floor(Math.random()*theArray.length);
    return theArray[randomIndex];
}

let colors: string[] = ['violet', 'indigo', 'blue', 'green'];
let randomColor: string = randomElem(colors);

现在,如果我尝试将randomColor的类型从string更改为number ,则会出现错误。 这证明在这种情况下,使用泛型要比使用any类型的安全得多。

TypeScript泛型错误

使用泛型似乎很有限

上一节讨论了如何使用泛型而不是any类型来编写单个函数并避免牺牲类型检查的好处。 我们在上一节中编写的泛型函数的一个问题是TypeScript不允许我们对传递给它的变量执行大量操作。

这是因为TypeScript无法对将预先传递给我们的泛型函数的变量类型做出任何假设。 因此,我们的通用函数只能使用适用于所有数据类型的那些操作。 下面的示例应该使这个概念更清楚。

function removeChar(theString: string, theChar: string): string {
    let theRegex = new RegExp(theChar, "gi");
    return theString.replace(theRegex, '');
}

上面的函数将从给定的字符串中删除所有出现的指定字符。 您可能要创建此函数的通用版本,以便还可以从给定数字中删除特定数字以及从字符串中删除字符。 这是相应的通用函数。

function removeIt<T>(theInput: T, theIt: string): T {
    let theRegex = new RegExp(theIt, "gi");
    return theInput.replace(theRegex, '');
}

removeChar函数未显示错误。 但是,如果在removeIt内使用replace ,TypeScript将告诉您类型'T'不存在replace 。 这是因为TypeScript不能再假定theInput将是一个字符串。

这种在泛型函数中使用不同方法的限制可能使您认为泛型的概念毕竟没有多大用处。 仅有少数几种方法必须适用于所有数据类型,才能在通用函数中使用它们,实际上并不能做很多事情。

在这一点上,您应该记住的一件重要事情是,您通常不需要创建将用于所有数据类型的函数。 创建将用于特定类型或特定范围的数据类型的函数更为常见。 对数据类型的这种限制使泛型函数更加有用。

使用约束创建通用函数

上一节中的通用removeIt函数显示了一个错误,因为其中的replace方法旨在与字符​​串一起使用,而传递给它的参数可以具有任何数据类型。

您可以使用extends关键字来约束传递给TypeScript中通用函数的数据类型。 但是, extends仅限于接口和类。 这意味着您创建的大多数通用函数将具有扩展基本接口或类的参数。

这是一个通用函数,可打印传递给它的人,家庭成员或名人的名字。

interface People {
    name: string
}

interface Family {
    name: string,
    age: number,
    relation: string
}

interface Celebrity extends People {
    profession: string
}

function printName<T extends People>(theInput: T): void {
    console.log(`My name is ${theInput.name}`);
}

let serena: Celebrity = {
    name: 'Serena Williams',
    profession: 'Tennis Player'
}

printName(serena);

在上面的示例中,我们定义了三个接口,每个接口都有一个name属性。 我们创建的泛型printName函数将接受任何扩展People对象。 换句话说,您可以将家庭或名人对象传递给此功能,并且它将打印其名称而不会引起任何投诉。 您可以定义更多接口,只要它们具有name属性,就可以使用printName函数而不会出现任何问题。

这是一个非常基本的示例,但是一旦您对整个过程感到满意,就可以创建更多有用的通用函数。 例如,您可以创建一个通用函数,计算给定月份中出售的不同商品的总价值,只要每个商品都具有可以存储价格的price属性和可以存储所sold商品数量的sold属性。 使用泛型,只要项目扩展相同的接口或类,就可以使用相同的功能。

最后的想法

在本教程中,我试图以一种初学者友好的方式介绍TypeScript中泛型的基础。 我们通过讨论对泛型的需求来开始本文。 之后,我们了解了使用泛型的正确方法,以便在不牺牲类型检查能力的情况下避免代码重复。 了解了此处讨论的基础知识之后,您可以在官方文档中阅读有关泛型的更多信息。

如果您有关于本教程的任何问题,我将很乐意在评论中回答。

翻译自: https://code.tutsplus.com/tutorials/typescript-for-beginners-part-5-generics--cms-29603

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值