软件工程中,我们不仅要创建一致的定义良好的 API ,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时提供了十分灵活的功能。 泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持

在代码编译阶段,我们在写代码不确定这个A变量的类型,虽然不确定,但想对不确定的A变量进行相同的处理。

这里的“类型”:不仅指数据类型,还可以指变量的类,还可以指变量的接口等等。

在代码运行阶段,这个变量肯定是已知的某个类型,这个时候,所谓的泛型才变成了固定的类型。

而泛型变量呢,是我们用来暂时取代变量A的类型的变量。

泛型变量的别称,类型变量。

类型不确定的情况,都是在函数传参的时候发生的,叫做泛型函数。
相应的,还有泛型类、泛型接口

1. 常见泛型变量

常见泛型变量代表的意思:

  • T(Type): 表示一个 TypeScript 类型
  • K(Key):表示对象中的键类型
  • V(Value):表示对象中的值类型
  • E(Element):表示元素类型

    这是约定俗成的用法。

2. 泛型函数

  1. // 要求传入的参数类型和返回类型一致
  2. // T 表示泛型,具体什么类型是调用这个方法的时候决定的
  3. function getData<T>(value: T): T {
  4. return value;
  5. }
  6. getData({ name: 'konsoue' })
  7. // 使用函数是,可以提前告知函数,泛型变量的值
  8. getData<number>(123);
  9. getData<string>('123');

现在我们添加了一个泛型变量 T 到函数中,这个变量允许我们获取,用户在使用这个函数时参数的类型。
这个函数里我们将 T 作为参数的类型和返回值的类型。

  1. function test<T, D>(val1: T, val2: D): D {
  2. return val2;
  3. }
  4. test(123, 22);
  5. test<number, string>(123, '22');
  1. // 交换函数
  2. function swap<T, U>(tuple: [T, U]): [U, T] {
  3. return [tuple[1], tuple[0]];
  4. }
  5. swap([1, '2'])

3. 泛型类

  1. class MinClass<T> {
  2. public list: Array<T> = [];
  3. add(item: T): void {
  4. this.list.push(item);
  5. }
  6. remove(item: T): void {
  7. this.list.splice(this.list.indexOf(item), 1);
  8. }
  9. }
  10. let test = new MinClass();
  11. test.add(2);
  12. test.add({ name: 'konsoue' });
  13. test.add('5');
  14. let num = new MinClass<number>();
  15. num.add(55);
  16. num.add(1)

明白了,上面的可以这样做,就能理解下面这段代码了

  • 把类作为参数来约束数据传入类型 ```typescript class User { userName: string | undefined; password: string | undefined; }

class MysqlDb { add(user: User): boolean { return true; } } let user = new User(); user.userName = ‘张三’; user.password = ‘123456’;

let db = new MysqlDb(); db.add(user);

  1. - 把类当作参数的泛型类
  2. ```typescript
  3. // 操作数据库的泛型类
  4. class MysqlDb<T> {
  5. add(info: T): boolean {
  6. return true;
  7. }
  8. }
  9. // 想给 user 表添加数据
  10. let db = new MysqlDb<User>();
  11. db.add(user);
  12. let db2 = new MysqlDb<ArticleCate>();
  13. db2.add(article);

4. 泛型接口

  1. // 普通函数接口
  2. interface ConfigFn {
  3. (value1: string, value2: string): [string, string];
  4. }
  5. const setData: ConfigFn = function (value1, value2) {
  6. return [value1, value2];
  7. }
  8. // 泛型接口写法一
  9. interface ConfigFn {
  10. <T>(value1: T, value2: T): [T, T];
  11. }
  12. const setData: ConfigFn = function (value1, value2) {
  13. return [value1, value2];
  14. }
  15. // 泛型接口写法二
  16. interface ConfigFn<T> {
  17. (value1: T, value2: T): [T, T];
  18. }
  19. const setData: ConfigFn<string> = function (value1, value2) {
  20. return [value1, value2];
  21. }

5. 约束泛型

我们可以对泛型进行一些约束,从而约束传进来的数据的类型。

  1. interface IWithLength {
  2. length: number;
  3. }
  4. // 限制传入的泛型类型具备 length 属性
  5. function echoWithLength<T extends IWithLength>(arg: T): T {
  6. console.log(arg.length);
  7. return arg;
  8. }
  9. const str = echoWithLength('str');
  10. const obj = echoWithLength({ length: 10 })
  11. const arr = echoWithLength([1, 2, 3])

6. 泛型工具类型

为了方便开发者, TypeScript 内置了一些常用的工具类型,比如 PartialRequiredReadOnlyRecordReturnType 等。

6.1 关键字

typeof 和 type

typeof 操作符可以用来获取一个变量声明或对象的类型。

  1. interface IUserProps {
  2. vType: 'userProps';
  3. name: string;
  4. email: string;
  5. }
  6. function isUserProps(x: any): x is IUserProps {
  7. return x.vType === 'userProps';
  8. }
  9. class UserProps {
  10. name: string;
  11. email: string;
  12. vType: 'userProps';
  13. constructor(name: string, email: string) {
  14. this.name = name;
  15. this.email = email;
  16. this.vType = 'userProps';
  17. }
  18. }
  19. const userProps: IUserProps = new UserProps('konsoue', 'xx@qq.com')
  20. type copyIuserProps = typeof userProps // copyIuserProps 就是 IUserProps
  21. const obj: copyIuserProps = {} // 报错。类型“{}”缺少类型“IUserProps”中的以下属性: vType, name, email

keyof

用来获取一个对象中的所有 key

  1. interface Person {
  2. name: string;
  3. age: number;
  4. }
  5. type K1 = keyof Person; // "name" | "age"
  6. type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
  7. type K3 = keyof { [x: string]: Person }; // string | number
  8. const a: K1 = { name: 'John', age: 15 } // x
  9. const a: K1 = 'name'; // √

in

用来遍历枚举类型

  1. type Keys = "a" | "b" | "c"
  2. type Obj = {
  3. [p in Keys]: any
  4. } // -> { a: any, b: any, c: any }

extends

有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束

  1. interface ILengthwise {
  2. length: number;
  3. }
  4. function loggingIdentity<T extends ILengthwise>(arg: T): T {
  5. console.log(arg.length);
  6. return arg;
  7. }
  8. loggingIdentity(3); // Error, number doesn't have a .length property
  9. // 需要传入符合约束类型的值,必须包含必须的属性
  10. loggingIdentity({length: 10, value: 3});

infer

在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。

  1. type ReturnType<T> = T extends (
  2. ...args: any[]
  3. ) => infer R ? R : any;

以上代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。

6.2 Partial

Partial<T> 的作用是将某个类型里的属性全部变为可选项。
源码中 Partial 的定义

  1. type Partial<T> = {
  2. [P in keyof T]?: T[P];
  3. };

使用例子

  1. interface Todo {
  2. title: string;
  3. description: string;
  4. }
  5. function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  6. return { ...todo, ...fieldsToUpdate };
  7. }
  8. const todo1 = {
  9. title: "organize desk",
  10. description: "clear clutter",
  11. };
  12. const todo2 = updateTodo(todo1, {
  13. description: "throw out trash",
  14. });
  15. /*
  16. fieldsToUpdate 的类型为 Partial<Todo>, 即
  17. {
  18. title?: string | undefined;
  19. description?: string | undefined;
  20. }
  21. */

6.3 Required

Required<T>将传入的属性变为必选项,,源码如下

  1. type Required<T> = {
  2. [P in keyof T]-?: T[P]
  3. };

-?:减掉可选 +?:加上可选。+ 可省略