TypeScript体操(二):Utility Type手写实现

前言

上一篇文章介绍了 typescript 类型体操中涉及到的基本概念,这篇文章我会将创建的utility Type进行实例讲解,并一一实现它。通过实践来深刻体会 , Utility Types 是一种特殊的类型,它们不是具体的值类型,而是可以对现有类型进行操作的类型。这些操作包括但不限于:选择属性、排除属性、从属性中提取类型等。

Just Do It!


在这里插入图片描述

正文开始如果觉得文章对您有帮助,请帮我三连+订阅,谢谢💖💖💖


常用 Utility Types 及其实现

TypeScript 提供了许多内置的 Utility Types, 下面我们只列举出了一些工作中常用的类型进行实现

Partial<T>

Partial<T> 将类型 T 的所有属性设置为可选。

  • 使用实例

    interface User {
      id: number;
      name: string;
      age: number;
    }
    
    const updateUser = (user: Partial<User>) => {
      // 可以只传递部分属性
      console.log(user);
    };
    
    updateUser({ name: "张三" });
    
  • 手写实现

    type MyPartial<T> = {
      [P in keyof T]?: T[P];
    };
    

Required<T>

Required<T> 将类型 T 的所有属性设置为必选。

  • 使用实例

    interface User {
      id: number;
      name: string;
      age: number;
    }
    
    const updateUser = (user: Required<User>) => {
      // 必须传递所有属性
      console.log(user);
    };
    
    updateUser({ id:1 , name: "张三", age:20 });
    
  • 手写实现

    type MyRequired<T> = {
      [P in keyof T]: T[P];
    };
    

Readonly<T>

Readonly<T> 使类型 T 的所有属性变为只读。

  • 使用实例

    interface User {
      id: number;
      name: string;
      age: number;
    }
    
    const user: Readonly<User> = {
      id: 1,
      name: "张三",
      age: 20,
    };
    
    // user.name = "李四"; // Error: name是自读属性, 不能进行赋值
    
  • 手写实现

    type MyReadonly<T> = {
      readonly [P in keyof T]: T[P];
    };
    

Pick<T, K>

Pick<T, K> 从类型 T 中选取部分属性,创建一个新的类型。

  • 使用实例

    interface User {
      id: number;
      name: string;
      age: number;
      email: string;
    }
    
    type UserNameAndEmail = Pick<User, "name" | "email">;
    
    const user: UserNameAndEmail = {
      name: "张三",
      email: "zhangsan@example.com",
    };
    
  • 手写实现

    type MyPick<T, K extends keyof T> = {
      [P in K]: T[P];
    };
    

Omit<T, K>

Omit<T, K> 从类型 T 中排除部分属性,创建一个新的类型。

  • 使用实例

    interface User {
      id: number;
      name: string;
      age: number;
      email: string;
    }
    
    type UserWithoutEmail = Omit<User, "email">;
    
    const user: UserWithoutEmail = {
      id: 1,
      name: "张三",
      age: 20,
    };
    
  • 手写实现

    type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
    
    // 不依赖其他
    type MyOmit<T, K> = {
      [Key in keyof T as Key extends K ? never : Key]: T[Key]
    }
    

Record<K, T>

Record<K, T> 创建一个类型,其键为 K 类型,值为 T 类型的字典。

  • 使用实例

    type Role = "admin" | "user" | "guest";
    
    interface User {
      id: number;
      name: string;
    }
    
    const users: Record<Role, User> = {
      admin: { id: 1, name: "管理员" },
      user: { id: 2, name: "普通用户" },
      guest: { id: 3, name: "访客" },
    };
    
  • 手写实现

    type MyRecord<K extends keyof any, T> = {
      [P in K]: T;
    };
    

Exclude<T, U>

Exclude<T, U> 从类型 T 中排除类型 U 的所有属性。

  • 使用实例

    type T = "a" | "b" | "c";
    type U = "a";
    
    type Result = Exclude<T, U>; // "b" | "c"
    
  • 手写实现

    type MyExclude<T, U> = T extends U ? never : T;
    

Extract<T, U>

Extract<T, U> 从类型 T 中提取出类型 U 的所有属性。

  • 使用实例

    type T = "a" | "b" | "c";
    type U = "a" | "c";
    
    type Result = Extract<T, U>; // "a" | "c"
    
  • 手写实现

    type MyExtract<T, U> = T extends U ? T : never;
    

NonNullable<T>

NonNullable<T> 从类型 T 中排除 nullundefined

  • 使用实例

    type T = string | number | null | undefined;
    
    type Result = NonNullable<T>; // string | number
    
  • 手写实现

    type MyNonNullable<T> = T extends null | undefined ? never : T;
    

ReturnType<T>

ReturnType<T> 提取函数类型 T 的返回类型。

  • 使用实例

    function getUser(): { id: number; name: string } {
      return { id: 1, name: "张三" };
    }
    
    type User = ReturnType<typeof getUser>; // { id: number; name: string }
    
  • 手写实现

    type MyReturnType<T extends (...args: any) => any> = T extends (
      ...args: any
    ) => infer R
      ? R
      : any;
    

InstanceType<T>

InstanceType<T> 提取构造函数类型 T 的实例类型。

  • 使用实例

    class User {
      constructor(public id: number, public name: string) {}
    }
    
    type UserInstance = InstanceType<typeof User>; // User
    
  • 手写实现

    type MyInstanceType<T extends new (...args: any) => any> = T extends new (
      ...args: any
    ) => infer R
      ? R
      : any;
    

Parameters<T>

Parameters<T> 提取函数类型 T 的参数类型组成的元组。

  • 使用实例

    function createUser(name: string, age: number) {
      return { name, age };
    }
    
    type Params = Parameters<typeof createUser>; // [string, number]
    
  • 手写实现

    type MyParameters<T extends (...args: any) => any> = T extends (...args: infer P) => any
      ? P
      : never;
    

ConstructorParameters<T>

ConstructorParameters<T> 提取构造函数类型 T 的参数类型组成的元组。

  • 使用实例

    class User {
      constructor(public name: string, public age: number) {}
    }
    
    type Params = ConstructorParameters<typeof User>; // [string, number]
    
  • 手写实现

    type MyConstructorParameters<T extends new (...args: any) => any> = T extends new (
      ...args: infer P
    ) => any
      ? P
      : never;
    

Awaited<T>

该类型用于模拟 async 函数中的 await 操作,或者 promise 上的 .then() 方法,独特之处在于,它们通过递归展开 promise 的方式获取异步返回的结果。
如果 推断的类型 F 仍未函数类型,则继续递归 F extends (value: infer V) => any ? Awaited<V> : never

  • 使用实例

    type A = Awaited<Promise<string>>; // type A = string
    type B = Awaited<Promise<Promise<number>>>; // type B = number
    type C = Awaited<boolean | Promise<number>>; // type C = number | boolean
    
  • 手写实现

    type MyAwaited<T> = T extends null | undefined ? T : T extends object & {
        then(onfulfilled: infer F): any;
    } ? F extends (value: infer V) => any ? MyAwaited<V> : never : T
    
    type A = MyAwaited<Promise<string>>; // type A = string
    type B = MyAwaited<Promise<Promise<number>>>; // type B = number
    type C = MyAwaited<boolean | Promise<number>>; // type C = number | boolean
    
    

结语

通过这篇文章,我们深入探讨了 TypeScript 中的 Utility Types 及其实现和应用。这些类型工具在实际开发中非常有用,能够帮助我们简化代码、提高类型安全性和可维护性。掌握这些技巧,能让你在处理复杂类型时游刃有余。希望这些内容对你有所帮助,并激发你在 TypeScript 类型体操方面的更多探索和实践。

  • 18
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ziyu_jia

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值