TypeScript 中的泛型,在函数中如何使用的

288 阅读3分钟

这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战

什么是泛型?

我想设计一个函数,参数为 number 类型,返回值也为 number 类型,TS 可以约束参数和返回值类型,这里你将参数和返回值类型都设置为 number 就可以了。

如果我预先不知道这个函数输入的类型是什么,只是希望不管这个函数传入的参数类型是什么,返回值类型都能和参数类型保持一致,这样能实现吗?

这个可以使用泛型来解决呀。

那我赶紧学习一下,泛型相关的知识吧。

泛型函数

泛型是 TS 中非常重要的概念,它极大的增强 TS 的类型表达能力。下面我们先来学习泛型在函数中,是如何使用的。

我们先来看看没有加泛型的函数 createArray,它接收两个参数,分别是值和数组的长度,然后在里面会创建一个新的数组,根据数组的长度,填充传入的值,这样的话,就可以返回一个每一项都是这个值的数组,比如,下面的输出结果。

function createArray(value, length) {
  let arr = [];
  for (let i = 0; i < length; i++) {
    arr[i] = value;
  }
  return arr;
}

let fooArray = createArray('foo', 3);
console.log(fooArray);
// ['foo', 'foo', 'foo']

下面我们给这个函数添加上类型,由于它的输入 value,是可以接收为任何类型的,所以它是 any 类型,length 是 number 类型,那它的返回值应该是每一项都是 any 类型的数组,但是这样做会有什么问题呢?

function createArray(value: any, length: number): any[] {
  let arr = [];
  for (let i = 0; i < length; i++) {
    arr[i] = value;
  }
  return arr;
}

let fooArray = createArray('foo', 3);
console.log(fooArray);
// ['foo', 'foo', 'foo']

可以看到 fooArray 是每一项都为 any 类型的数组,那么我们在取用它的某一项的时候,就无法预知它会有什么属性或者方法。但是其实,我们这个时候传入的是 string 类型。怎么办呢?

我们可以添加上泛型,使用尖括号的格式,然后将所有 any 类型都替换成 T,然后再调用函数的时候,也是使用尖括号的形式,传入一个 string 类型。

改造后:

function createArray<T>(value: T, length: number): T[] {
  let arr = [];
  for (let i = 0; i < length; i++) {
    arr[i] = value;
  }
  return arr;
}

let fooArray = createArray<string>('foo', 3);
console.log(fooArray);
// ['foo', 'foo', 'foo']

通过上面操作,我们可以看到 createArray 函数的输入是 string,返回是 string 类型的数组。

可以看到它的每一项都变成了 string 类型,并且有了 string 的属性和方法。

image.png

使用泛型的优势,就在于我们可以动态调整这个类型,比如说,传入的是数字的话,我们可以修改成下面这样,这样的话,它就变成每一项为 number 类型的数组了。

let fooArray = createArray<number>(1, 3);

习题:求函数 animalInfo 的返回值类型

const animalInfo = <T>(arg: T) _____ => {
  return {
    age: arg,
    name: 'panda'
  };
};

animalInfo<string>('10');
animalInfo<number>(10);

// A
{
  age: string;
  name: string;
}
// B
{
  age: number;
  name: string;
}
// C
{
  age: T;
  name: string;
}

// D
{
  age: any;
  name: string;
}

答案:

C

解析:

函数 animalInfo 的返回值为

{
    age: arg,
    name: 'panda'
}

所以只需要确定变量 arg 的类型即可。arg 参数中已经确定了泛型 T。所以答案选 C

资料:泛型的命名

泛型是一种很自由的类型,我们一般用 T 来表示泛型,但这不是必须的。 我们可以根据自身业务或者需求来约定好泛型的命名规则,例如 Google 团队在 Google Java Style Guide 中就推荐了两种命名方式:

Each type variable is named in one of two styles:

  • A single capital letter, optionally followed by a single numeral (such as E, T, X, T2)
  • A name in the form used for classes (see Section 5.2.2, Class names), followed by the capital letter T (examples: RequestT, FooBarT).

附:常用单字母泛型命名规则

字母类型场景
E元素
K
V
N数字
T类型主要用于表示第一个参数
S类型主要用于表示第二个参数
U类型主要用于表示第三个参数
V类型主要用于表示第四个参数