TypeScript内置的Pick和Omit,简直是处理对象类型时的瑞士军刀。但你有没有想过,它们是怎么变出来的?今天咱们就拆开看看,自己动手实现一遍。理解了原理,你就能创造出适合自己项目的定制化工具类型。
![图片[1]-如何实现Pick、Omit等工具类型?-速码派](http://www.sumapai.com/wp-content/uploads/2026/01/a8fd095f6a4946f0bed6afe492d24486_tplv-tb4s082cfz-aigc_resize_1080_1080-1024x683.webp)
先看Pick。它的作用是从一个对象类型T里,挑出指定的几个属性K,组成一个新类型。用起来是这样的:Pick<User, 'id' | 'name'>。自己实现的话,核心是“映射类型”加上“属性筛选”。
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
这里有两层关键点。第一,K extends keyof T是约束,它确保你传入的属性名K必须是T的键之一,防止你瞎选。第二,[P in K]: T[P]是一个映射类型,它遍历联合类型K中的每一个属性名P,然后在新类型里创建这个属性,其类型取自原类型T中对应键的类型T[P]。就这么简单,遍历加上按索引访问类型。
再来看看更有趣的Omit。它的作用是反着来:从一个对象类型T里,排除掉指定的几个属性K,剩下的属性组成新类型。比如Omit<User, 'password' | 'token'>。它的实现需要一点点“组合技”。
type MyOmit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P];
};
这里用到了Exclude这个内置工具类型(它本身也是个条件类型)。keyof T得到T的所有键组成的联合类型,然后用Exclude<keyof T, K>从这个联合类型中剔除掉你想排除的键K。得到的结果就是“剩下的键”的联合类型。最后,再用映射类型遍历这个“剩下的键”集合,把每个属性及其类型从T里拷贝过来。你看,Omit其实是Pick思想的反向应用,中间用Exclude做了个过滤。
理解了这两个,你可以玩出更多花样。比如,实现一个Nullable<T>,把对象的所有属性都变成可选的:
type Nullable<T> = {
[P in keyof T]?: T[P] | null;
};
或者一个ReadonlyPartial<T>,让所有属性既是只读又是可选的(虽然实战中可能不常用):
type ReadonlyPartial<T> = {
readonly [P in keyof T]?: T[P];
};
映射类型的强大之处在于,它让你能像操作运行时数据一样,去批量操作类型。in关键字负责遍历,keyof拿到钥匙串,索引访问T[P]拿到对应的类型值。结合条件类型,你几乎能对类型做任何转换。
自己写工具类型时,有个小技巧:多用约束(extends)来保证输入是合理的。比如K extends keyof T。另外,从简单的需求开始,先实现一个只满足当前场景的,再慢慢抽象成通用的。别一上来就想写一个万能类型,容易把自己绕晕。
看懂了这些,你就不再是类型系统的使用者,而是它的设计者了。下次当你在项目里发现重复的类型转换逻辑时,别复制粘贴,试着把它封装成一个你自己的MyAwesomeUtility<T>。


























暂无评论内容