什么是 TypeScript 泛型编程

1,166 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前端的程序员可能对泛型不是很了解,了解Java这种强类型语言可能会知道泛型,使用泛型可以编写动态可重用的TS代码,在TS中泛型一般用于类、接口和函数。

今天我们就来学习在TypeScript中如何去使用泛型,以及一般会用到泛型的场景。

1. 快速理解泛型

可能很多熟悉JS开发的前端搞不懂泛型是啥,下面我们通过一个最简单的例子来明白泛型。

这两个功能相同但只是入参和返回的类型不同,如果在TS中不使用泛型的话,就要定义两个函数,只是它的类型不同。

// 数字类型
function fun(args: number): number {
  return args;
}

// 字符串类型
function fun(args: string): string {
  return args;
}

当然你可以说用any类型啊

function fun(args: any): any {
  return args;
}

但使用any其实是逃避类型检查,和使用JS一样,那还用TS干嘛。

下面我们使用泛型来实现

2. 使用泛型

要创建一个泛型,需要使用Type参数,类型参数一般用T或<T>定义,表示类、接口或者函数的数据类型。

下面我们用泛型改写上面的函数

function fun<T>(args:T):T {
  return args;
}

使用泛型函数也很简单

let result = fun<string>("Hello World");

或者这样

let result2 = fun<number>(200);

我们可以去跑一下代码验证一下,可以去官方的playground

地址:www.typescriptlang.org/play

image-20211016191705042

以上是泛型在TS中最简单用法,平时也挺常用的。

下面来看看稍微复杂点的泛型用法

3. 多种类型的泛型

上面我们使用T来表示一个类型,那么如果一个函数有很多参数,我们可以用不同的字母来表示类型。比如下面这样

function fun<T, U, V>(args1:T, args2: U, args3: V): V {
  return args3;
}

上面的函数接收args1、args2、args3共3个参数,并返回args3。这些参数不限定类型,因为T, U, V被用作函数参数的泛型类型。

下面调用这个函数

let result3 = fun<string, number, boolean>('小帅', 23, false);

然后我们运行一个试试

image-20211016193120336

函数返回第三个参数,所以是false

泛型不光可以用于函数,也可以用于类和接口,下面我们来继续看

4. 创建泛型类

和函数差不多,类的泛型也使用<>中的类型参数,然后在整个类中使用<T>类型来定义方法和属性。

我们来创建一个自定义数组的类,定义一个arr数组属性,数组的类型为一个泛型

class customArray<T> {
  private arr: T[] = [];
}

创建一个获取该数组的方法

getItems (): T[] {
  return this.arr;
}

再来创建一个addItem方法,用来向数组中push元素

addItem(item: T) {
  this.arr.push(item);
}

arr数组的类型为T[],所以我们的数组可以为任意类型,可以是数字、字符串、布尔等任意类型。

接着在添加一个removeItem方法,用于删除数组的指定元素

removeItem(item: T) {
  let index = this.arr.indexOf(item);
  if(index > -1) {
    this.arr.splice(index, 1);
  }
}

现在类的属性和方法写的差不多了,我们来测试一下结果如何

let numObj = new customArray<number>();
numObj.addItem(10);

let strObj = new customArray<string>();
strObj.addItem('小帅');

console.log(numObj);
console.log(strObj);

console.log(numObj.getItems());
console.log(strObj.getItems());

我们创建了一个数字类型的numObj对象,一个字符串类型的strObj对象

image-20211016195033118

下面我们来了解关于泛型的一些约束力

5. 泛型的约束

上面我们学会了怎么给函数和类去使用泛型作为类型,但使用泛型也有一些缺点,比如下面的例子

function getLength<T>(args: T) : number {
  return args.length;
}

在上面的函数中,如果我们传入的参数有length属性会正常工作,如果没有就会报错

image-20211016200319057

image-20211016200341944

可以看到错误为泛型T并没有length属性,那我们怎么办呢

我们可以用一个通用的约束,创建一个名为 funcArgs 的接口并定义length属性

interface funcArgs {
  length: number;
}

现在我们来修改一下

function getLength<T extends funcArgs>(args:T) : number {
  return args.length;
}

下面我们来跑一下

image-20211016201808618

可以看到,使用没有length参数的类型,TS就会抛出异常。

如果参数具有length属性,就不会有任何错误消息。

总结

今天我们学习了如何在TypeScript中使用泛型,包括在函数、类、接口应用泛型等。

如果本文有帮助,微信搜索【小帅的编程笔记】,每天一个小知识