如何从 TypeScript 类型中排除属性:为代码减负
\n\n您是否遇到过这种情况:有一个大型接口或类型,但需要创建一个几乎相同、只是少了一两个字段的新类型?复制全部代码?复制 15 行然后删除一行?有一种更优雅的方法。今天,我们将探讨如何在 TypeScript 中从类型中排除属性——这是最流行的工具之一,可以避免重复并使代码更简洁。
\n\n这个话题在 Stack Overflow 上非常热门。“Exclude property from type”这个问题获得了数千次浏览,因为每个开发者迟早都会遇到需要从类型中删除字段或从接口中排除属性的情况。让我们从简单示例开始,逐步深入到高级技巧。
\n\n问题:为什么要从类型中排除字段?
\n\n假设您有一个 User 接口:
interface User {\n id: number;\n name: string;\n email: string;\n password: string;\n createdAt: Date;\n}\n\n您想创建一个注册表单,用户在其中输入姓名、电子邮件和密码。但 id 由服务器生成,createdAt 是自动生成的。您需要一个只包含 name、email 和 password 的 NewUser 类型。如何实现?
一个糟糕的解决方案是重新声明一个新接口:
\n\ninterface NewUser {\n name: string;\n email: string;\n password: string;\n}\n\n这虽然可行,但违反了 DRY(不要重复自己)原则。如果我们在 User 中更改 name 的类型,也必须更改 NewUser 中的对应类型。这正是从类型中排除属性发挥作用的地方。
工具类型 Omit:主要工具
\n\nTypeScript 提供了一个内置工具类型 Omit<Type, Keys>。它通过从原始类型中排除指定的键来创建一个新类型。语法很简单:
type NewUser = Omit<User, 'id' | 'createdAt'>;\n\n现在,NewUser 只包含 name、email 和 password。如果我们在 User 中将 email 改为 Email,NewUser 会自动更新。
让我们实际测试一下:
\n\nconst newUser: NewUser = {\n name: '安娜',\n email: 'anna@example.com',\n password: 'secure123'\n}; // ✅ 一切正常\n\nconst invalidUser: NewUser = {\n name: '安娜',\n email: 'anna@example.com',\n password: 'secure123',\n id: 1 // ❌ 错误:id 在 NewUser 类型中不存在\n};\n\n请注意: Omit 既适用于接口也适用于类型。它是从类型中排除字段的通用工具。
Omit 内部是如何工作的?
\n\n为了更好地理解 TypeScript 如何从类型中排除属性,让我们深入了解一下内部机制。Omit 是另外两个工具的组合:Pick 和 Exclude。
它在 TypeScript 标准库中的定义如下:
\n\ntype Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;\n\n逐步解析:
\n- \n
keyof T— 获取类型 T 的所有键(例如'id' | 'name' | 'email' | 'password' | 'createdAt') \n Exclude<keyof T, K>— 从这些键中排除 K 中指定的键(得到'name' | 'email' | 'password') \n Pick<T, ...>— 从 T 中只选取剩余的键 \n
因此,从接口中排除属性本质上就是键的过滤。很优雅,对吧?
\n\nPick:Omit 的另一面
\n\n有时,与其排除,不如反过来