【TS】泛型的使用

104 阅读3分钟
  • 在TypeScript(TS)中,泛型是一种非常有用的特性,它允许我们创建可重用的组件,这些组件可以处理多种类型的数据而不是单一的数据类型。

  • 以下是一些在TypeScript中常用的泛型:

  1. 泛型函数
    function fun<T>(arg: T): T {
        return arg;
    }
    
  2. 泛型接口
    interface DemoInterface<T> {
        (arg: T): T;
    }
    
  3. 泛型类
    class DemoClass<T> {
        zeroValue: T;
        add: (x: T, y: T) => T;
    }
    
  4. 泛型约束
    interface DemoInterface {
        length: number;
    }
    function demoFun<T extends DemoInterface>(arg: T): T {
        // 现在知道它有length属性,所以没有更多的错误
        console.log(arg.length);  
        return arg;
    }
    
  5. 泛型工具类型
    • 泛型工具类型的使用场景非常广泛,它们提高了代码的灵活性和可重用性,同时保持了类型安全性。

    • 在实际开发中,根据具体的需求,你可以组合使用这些工具类型来创建更复杂的类型。

    • Partial<T>:将T的所有属性转换为可选的。

      type Partial<T> = {
        [P in keyof T]?: T[P];
      };
      
      • 当你想要将一个对象的所有属性变为可选的,例如,在更新一个对象的部分属性时。
      interface Todo { 
          title: string; 
          description: string; 
      } 
      function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) { 
          return { ...todo, ...fieldsToUpdate }; 
      } 
      const todo1 = { 
          title: "organize desk", 
          description: "clear clutter", 
      }; 
      updateTodo(todo1, { description: "throw out trash" });
      
    • Readonly<T>:将T的所有属性转换为只读的。

      type Readonly<T> = {
        readonly [P in keyof T]: T[P];
      };
      
      • 当你想要确保对象在创建后不会被修改,例如,创建一个不可变的数据结构。
      interface Config {
          readonly host: string;
      }
      
      const config: Readonly<Config> = {
          host: "localhost",
      };
      
      // 错误:不能分配给'host',因为它是一个只读属性。
      // config.host = "example.com";
      
    • Pick<T, K>:从T中选择一组属性K来构造一个新的类型。

      type Pick<T, K extends keyof T> = {
        [P in K]: T[P];
      };
      
      • 当你需要从一个类型中选取一组属性来构造一个新的类型,例如,从表单提交的数据中提取需要发送到后端的部分
      interface User {
          id: number;
          name: string;
          email: string;
      }
      
      type UserWithoutId = Pick<User, "name" | "email">;
      
      function saveUserDetails(userDetails: UserWithoutId) {
          // 保存不带id的详细信息
      }
      
    • Record<K, T>:构造一个类型,其属性名的类型为K,属性值的类型为T。

      type Record<K extends keyof any, T> = {
        [P in K]: T;
      };
      
      • 当你想要创建一个对象类型,其键和值的类型都是确定的,例如,一个映射对象。
      const namesById: Record<number, string> = {
        1: "Alice",
        2: "Bob",
        3: "Charlie",
      };
      
      function getNameById(id: number): string {
        return namesById[id];
      }
      
    • Exclude<T, U>:从T中排除那些可以赋值给U的类型(返回差集)。

      type Exclude<T, U> = T extends U ? never : T;
      
      • 当你想要从一个联合类型中排除某些类型,例如,从函数的参数类型中排除某些值。
      type AvailableColors = "red" | "green" | "blue";
      type ChosenColors = "red" | "blue";
      
      // "green" 是唯一有效的值
      const color: Exclude<AvailableColors, ChosenColors> = "green";
      
    • Extract<T, U>:提取T中可以赋值给U的那些类型(返回交集)。

      type Extract<T, U> = T extends U ? T : never;
      
      • 当你想要从一个联合类型中提取某些类型,例如,找到两个接口共有的属性。
      interface FirstType {
       id: number;
       name: string;
       age: number;
      }
      
      interface SecondType {
       id: number;
       name: string;
       gender: string;
      }
      
      // CommonProperties 将为 "id" | "name"
      type CommonProperties = Extract<keyof FirstType, keyof SecondType>; 
      
    • NonNullable<T>:从T中排除null和undefined。

      type NonNullable<T> = T & {};
      
      • 当你想要从类型中排除nullundefined,例如,在处理函数返回值时。
      type Foo = string | number | null | undefined;
      type NonNullableFoo = NonNullable<Foo>;
      
      function assertNotNull<T>(value: T): NonNullable<T> {
       if (value === null || value === undefined) {
         throw new Error("值不能为 null 或 undefined");
       }
       return value;
      }
      
  • 泛型在TypeScript中非常强大,它们提供了一种方法来创建可重用的代码组件,同时还能保持类型安全。

  • 通过使用泛型,可以编写更加灵活和可维护的代码。