TypeScript 学习笔记 泛型 Generics


prtyaa
prtyaa 2023-12-26 17:27:58 64971
分类专栏: 资讯

什么是泛型?

我们已经知道了 TypeScript 为变量增加了类型声明,例如 string, number, boolean 等。而泛型可以让你为函数,类和接口定义可复用的通用类型。

用简单的例子来介绍会更加容易理解。

例如我们要编写一个函数返回数字数组中随机一个元素:

function getRandomNumberElement(items: number[]): number {
    let randomIndex = Math.floor(Math.random() * items.length);
    return items[randomIndex];
}
let numbers = [1, 5, 7, 4, 2, 9];
console.log(getRandomNumberElement(numbers));

假设你需要从一个字符串数组中返回随机一个字符串,这次可以这样实现:

function getRandomStringElement(items: string[]): string {
    let randomIndex = Math.floor(Math.random() * items.length);
    return items[randomIndex];
}
let colors = ['red', 'green', 'blue'];
console.log(getRandomStringElement(colors));

如果还有其他需求例如从一个对象数组中随机返回一个元素,每次都编写一个函数这种方式就不具备扩展性了。

其中一个解决办法是使用 any 类型,如下:

function getRandomAnyElement(items: any[]): any {
    let randomIndex = Math.floor(Math.random() * items.length);
    return items[randomIndex];
}
let numbers = [1, 5, 7, 4, 2, 9];
let colors = ['red', 'green', 'blue'];

console.log(getRandomAnyElement(numbers));
console.log(getRandomAnyElement(colors));

这样可以正常工作,不过返回值是 any,此时失去了类型检查的作用。例如:

let ele: string;
ele = getRandomAnyElement(numbers);
console.log(typeof ele); // number

虽然 ele 声明的类型是字符串,但是最后赋值的类型却是数字。

引入泛型

在 TypeScript 中,可以用泛型来表示两个值是同一种类型。此时可以在函数签名中声明一个类型参数如下:

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

这个函数使用了类型变量 T,这个 T 可以获取在调用函数时提供的类型。此外,这个函数也用了 T 类型变量作为它的返回值类型。

这个 getRandomElement 函数是泛型函数,因为它支持所有类型数据组成的数组,例如字符串,数字,对象等。

这里的字母 T 是一般的约定,不过你也可以自由选择其他名字。

调用泛型函数

我们可以这样调用:

let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement<number>(numbers); 
console.log(randomEle);

这个例子在调用 getRandomElement() 函数时显式传递了 number 作为 T 的类型。

实际上我们无需这样做,可以让 TypeScript 编译器根据传递的参数类型自动设置,如下:

let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement(numbers); 
console.log(randomEle);

此时,getRandomElement() 函数也可以执行类型强校验。例如把返回值赋值给一个字符串变量就会报错,如下所示:

let numbers = [1, 5, 7, 4, 2, 9];
let returnElem: string;
returnElem = getRandomElement(numbers);  // compiler error

有多个类型的泛型函数

如下展示了如何编写一个有两个类型变量 U 和 V 的泛型函数:

function merge<U, V>(obj1: U, obj2: V) {
    return {
        ...obj1,
        ...obj2
    };
}

TypeScript 会推断出 merge() 函数的返回值是 U 和 V 的交集:U & V。

let result = merge(
    { name: 'John' },
    { jobTitle: 'Frontend Developer' }
);

console.log(result); // { name: 'John', jobTitle: 'Frontend Developer' }

引入泛型约束

继续以上面的 merge() 函数为例,假设调用时第二个参数不是一个对象,而是一个数字:

let person = merge(
    { name: 'John' },
    25
);

console.log(person);

它会输出 { name: 'Joh' } ,不会报错。

如果要对参数进行约束,要求它们必须是对象类型。此时可以使用 extends 关键字,例如:

function merge<U extends object, V extends object>(obj1: U, obj2: V) {
    return {
        ...obj1,
        ...obj2
    };
}

修改 merge() 函数后再次运行就会报错:

Argument of type '25' is not assignable to parameter of type 'object'.

在泛型约束中使用类型参数

TypeScript 还允许声明一个类型参数受另一个类型参数约束。

例如,如下的 prop() 函数有两个参数,其中第二个参数必须是第一个参数的属性:

function prop<T, K>(obj: T, key: K) {
    return obj[key];
}

此时编译器会报错:

Type 'K' cannot be used to index type 'T'.

要解决这个问题,为 K 增加一个约束确保它是 T 的一个 key:

function prop<T, K extends keyof T>(obj: T, key: K) {
    return obj[key];
}

此时便可以正常工作了:

let str = prop({ name: 'John' }, 'name');
console.log(str); // John

如果第二个参数传一个不存在的 key 则会报错:

let str = prop({ name: 'John' }, 'age');

错误:

Argument of type '"age"' is not assignable to parameter of type '"name"'.

泛型类

在 TypeScript 中类也可以是泛型。使用 new 实例化一个泛型类时,它的类型参数同样可以推断出来:

class Box<Type> {
  contents: Type;
  constructor(value: Type) {
    this.contents = value;
  }
}
 
const b = new Box("hello!");

在这个例子中 TypeScript 可以推测得知 Type 为字符串类型。

需要注意的是静态属性不能使用类型参数,因为它的类型是固定的,不应该随着参数而更改。

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=30922
赞同 0
评论 0 条
prtyaaL0
粉丝 1 发表 2554 + 关注 私信
上周热门
银河麒麟添加网络打印机时,出现“client-error-not-possible”错误提示  1448
银河麒麟打印带有图像的文档时出错  1365
银河麒麟添加打印机时,出现“server-error-internal-error”  1151
统信桌面专业版【如何查询系统安装时间】  1073
统信操作系统各版本介绍  1070
统信桌面专业版【全盘安装UOS系统】介绍  1028
麒麟系统也能完整体验微信啦!  984
统信【启动盘制作工具】使用介绍  627
统信桌面专业版【一个U盘做多个系统启动盘】的方法  575
信刻全自动档案蓝光光盘检测一体机  483
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
信创开放社区邀请他人注册的具体步骤如下 15
如何玩转信创开放社区—从小白进阶到专家 15
方德桌面操作系统 14
我有15积分有什么用? 13
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

添加我为好友,拉您入交流群!

请使用微信扫一扫!