Partial、Required、Pick、Omit、Record 分别解决什么问题
What problems do Partial, Required, Pick, Omit and Record each solve
一句话:五个都是「按规则从已有类型造新类型」的 工具类型(utility types):Partial 全变可选、Required 全变必填、Pick 只留白名单、Omit 去掉黑名单、Record 按键集合造字典。 考点不是背 API,是看到场景知道伸手拿哪个。
三个高频场景:
- 更新函数的 patch 参数 →
Partial<User>。调用方只传要改的字段,但传了的字段类型必须对, 拼错字段名照样编译报错 (Object literal may only specify known properties)。 这是它比Record<string, unknown>强的地方: 可选,但没有放弃字段级检查。 - 从大类型裁 props →
Pick<User, "name" | "role">。组件只声明真正用到的字段,User以后加字段不会波及它。 反方向的「去掉不该外泄的字段」用Omit<User, "email">。 - 键集合已知的字典 →
Record<Theme, string>, 别用索引签名。键是字面量联合(literal union)时,Record 的键集合是封闭的: 少一个键、多一个键都是编译错误。 索引签名对任何 string 键都放行,还谎称读出来的值一定存在。
Pick 和 Omit 怎么选:白名单还是黑名单。 类型以后会加字段时,Omit 会把新字段自动带进来 —— 透传配置时这是优点,做脱敏时这是漏洞。 所以对外暴露的公开类型建议用 Pick: 新加的敏感字段默认不外泄。
会追问:「Partial 是深的还是浅的?」—— 浅的, 只动第一层,嵌套对象内部照样必填;要深的得自己写递归的 mapped type。 「Record<string, T> 和索引签名什么区别?」—— 几乎等价,但 Record 的键能用字面量联合,索引签名不行。 答完这两问,多半会接一句「那 Partial 自己怎么实现?」—— 那就是下一题。
In one line: all five are utility types — they build a new type from an existing one by a rule: Partial makes everything optional, Required makes everything required, Pick keeps a whitelist, Omit drops a blacklist, Record builds a dictionary from a key set. The real question is not the API — it is knowing which one a scenario calls for.
Three scenarios that keep coming up:
- The patch parameter of an update function →
Partial<User>. Callers pass only the fields they change, but every field they do pass is still checked — a typo still fails to compile (Object literal may only specify known properties). That is what makes it stronger thanRecord<string, unknown>: optional, without giving up per-field checking. - Trimming props from a big type →
Pick<User, "name" | "role">. The component declares only the fields it actually uses, so adding fields toUserlater cannot ripple into it. The opposite direction — dropping fields that must not leak — isOmit<User, "email">. - A dictionary with a known key set →
Record<Theme, string>, not an index signature. With a literal union as the key, Record is a closed key set: one key missing or one key extra is a compile error. An index signature waves any string through, then claims the value definitely exists.
Choosing between Pick and Omit: whitelist or blacklist. When the type will grow, Omit silently carries every new field along — a feature when forwarding config, a hole when redacting. So for public-facing types, prefer Pick: a newly added sensitive field stays private by default.
Follow-up: “Is Partial deep or shallow?” — shallow. It only touches the first level; properties inside nested objects stay required. A deep version means writing a recursive mapped type yourself. “What is the difference between Record<string, T> and an index signature?” — almost none, except Record keys can be literal unions and index signatures cannot. Answer both, and the next question is usually “so how is Partial itself implemented?” — which is exactly the next card.