深入学习 TS 高级类型 Pick,让你也能在 TS 类型中实现排除法 🙈🙈🙈

Pick 是 TypeScript 中的一个实用类型,用于从已有的类型中选择一组属性,生成一个新的类型。它可以帮助我们在代码中更精细地控制类型,从而提高代码的类型安全性和可维护性。

基本使用

假设我们有一个 User 类型:

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

我们想创建一个新的类型,只包含 name 和 email 属性。我们可以使用 Pick 来实现:

ts 复制代码
type UserContactInfo = Pick<User, "name" | "email">;

const contactInfo: UserContactInfo = {
  name: "Moment",
  email: "moment@qq.com",
};

实际的应用

Pick 在实际开发中非常有用,尤其是在处理大型接口或模型时。常见的应用场景包括:

  1. 选择性暴露接口,在 API 返回值或前端组件中,只暴露必要的属性:
ts 复制代码
type PublicUserInfo = Pick<User, "name" | "email">;
  1. 简化类型定义,在处理复杂的类型或接口时,可以简化类型定义,避免重复代码:
ts 复制代码
interface CreateUserRequest extends Pick<User, "name" | "email" | "age"> {}
  1. 类型约束,在函数参数中,只允许特定的属性:
ts 复制代码
function sendEmail(user: Pick<User, "email">) {}

实现原理

Pick 类型的实现原理如下所示:

ts 复制代码
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

在上面的代码中:

  1. T 是源类型。

  2. K 是一个属性名的联合类型,表示要从 T 中选取的属性。

  3. P in K 表示循环 K 中的每个属性 P,并将其添加到新的类型中。

  4. T[P] 表示 T 类型中属性 P 的类型。

进阶使用

结合泛型函数使用

我们可以定义一个泛型函数,使用 Pick 来选择参数类型的部分属性:

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  address: string;
}

function getUserInfo<T extends keyof User>(
  user: User,
  keys: T[]
): Pick<User, T> {
  const result = {} as Pick<User, T>;
  keys.forEach((key) => {
    result[key] = user[key];
  });
  return result;
}

const user: User = {
  id: 1,
  name: "Moment",
  email: "moment@qq.com",
  age: 18,
  address: "西安",
};

const userInfo = getUserInfo(user, ["name", "email"]);
console.log(userInfo);

最终输出结果如下所示:

动态选择属性

通过结合条件类型、映射类型、索引类型查询、索引访问类型和工具类型,实现查找出接口中的可选类型:

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  address?: string;
}

// 判断属性是否为可选
type IsOptional<T, K extends keyof T> = {} extends Pick<T, K> ? K : never;

// 提取可选属性的键,并排除掉 undefined 类型
type OptionalKeys<T> = Exclude<
  {
    [K in keyof T]: IsOptional<T, K>;
  }[keyof T],
  undefined
>;

// 使用 Pick 提取可选属性的类型
type UserOptionalProperties = Pick<User, OptionalKeys<User>>;

const userOptionalProps: UserOptionalProperties = {
  address: "广州",
};

console.log(userOptionalProps);

在上面的代码中,条件类型允许我们根据类型的特性来选择不同的类型。条件类型的基本形式是 T extends U ? X : Y,意思是如果类型 T 能够赋值给类型 U,则结果是类型 X,否则是类型 Y。

ts 复制代码
type IsOptional<T, K extends keyof T> = {} extends Pick<T, K> ? K : never;

这里 {} extends Pick<T, K> 检查 {} 是否能赋值给 Pick<T, K>,即属性 K 是否可选。如果可选,结果为 K,否则为 never。

深层属性选择

使用递归类型,可以实现从嵌套对象中选择属性。

ts 复制代码
interface Address {
  street: string;
  city: string;
}

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  address: Address;
}

// 递归地选择深层属性
type DeepPick<T, K extends keyof T> = {
  [P in K]: T[P] extends object ? DeepPick<T[P], keyof T[P]> : T[P];
};

type UserAddressOnly = DeepPick<User, "address">;

const example: UserAddressOnly = {
  address: {
    street: "中山大道",
    city: "广州",
  },
};

条件类型与 Pick 结合使用

我们可以使用条件类型来选择符合特定条件的属性:

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  isActive: boolean;
}

// 选择布尔类型的属性
type BooleanProperties<T> = {
  [K in keyof T]: T[K] extends boolean ? K : never;
}[keyof T];

type UserBooleanProperties = Pick<User, BooleanProperties<User>>;

const boo: UserBooleanProperties = {
  isActive: false,
};

最终类型如下图所示:

结合 Exclude 使用

我们可以结合 Pick 和 Exclude 来实现复杂的类型选择:

ts 复制代码
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  address: string;
  isActive: boolean;
}

// 排除特定类型的属性
type ExcludeProperties<T, U> = {
  [K in keyof T]: T[K] extends U ? never : K;
}[keyof T];

type NonStringProperties<T> = Pick<T, ExcludeProperties<T, string>>;

type UserNonStringProperties = NonStringProperties<User>;

const user: UserNonStringProperties = {
  age: 18,
  id: 7,
  isActive: true,
};

最终类型如下图所示:

总结

Pick 是 TypeScript 中一个强大的实用类型,允许你从现有类型中选择特定的属性,创建新的类型。

通过这些高级用法示例,我们可以看到 Pick 类型在 TypeScript 中的强大和灵活性。合理使用这些技术可以帮助你编写更健壮和可维护的类型安全代码。

相关推荐
崔庆才丨静觅14 分钟前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60611 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了1 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅1 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅2 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅2 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment2 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax