十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

TS 工具类型实战:Pick/Omit/Partial/Record 一次讲透,别再 Omit 反了

TS 工具类型实战:Pick/Omit/Partial/Record 一次讲透,别再 Omit 反了 为什么需要工具类型类型写得好的项目维护起来像看文档类型写得烂的项目改一行代码要猜半小时。一个User接口可能被用在列表、详情、表单、更新、权限等多个场景每个场景需要的字段都不一样。如果每个场景都手写interface不仅重复还容易漏改。TypeScript 内置的工具类型就是帮你“消费”已有类型快速生成新类型。keyof一切工具类型的地基keyof T会取出T所有键组成联合类型。interface User { id: number; name: string; age: number; email: string; } type UserKeys keyof User; // id | name | age | email你会发现keyof是后面所有操作的“原料”没有它你都不知道类型里有哪些键。Pick按需取字段Pick 的字面意思就是“挑”从一个大类型里挑出你需要的字段形成新类型。type UserPreview PickUser, id | name; const u: UserPreview { id: 1, name: lg };场景列表项通常只需要id name不需要email、age这些敏感或大字段详情接口返回的 DTO 也可以用Pick缩小范围。说白了Pick 就是给类型做“字段精选”。Omit反向剔除字段Omit 和 Pick 相反是“不要哪些字段”。type UserSafe OmitUser, email; const safeUser: UserSafe { id: 2, name: l, age: 20 };场景用户信息要脱敏不发email给前端更新用户时排除idid由路径参数决定。Omit 的内部实现为什么等价于 Pick ExcludeTS 源码中 Omit 的实现是type OmitT, K extends keyof any PickT, Excludekeyof T, K;怎么理解拆成三步keyof T拿到所有键的联合类型例如id | name | age | emailExcludekeyof T, K把要剔除的K从联合类型中删掉剩下需要保留的键PickT, 剩下的键再把这些键从T中挑出来所以OmitUser, email等价于PickUser, Excludekeyof User, email也就是PickUser, id | name | age。一句话Omit 不是新魔法它只是 Pick Exclude 的组合拳。Exclude 和 Omit 的区别Exclude联合类型, 要排除的成员从联合类型中删除某些成员返回新的联合类型。Omit对象类型, 要排除的键从对象类型中删除某些键返回新的对象类型。Exclude 处理的是联合类型Omit 处理的是对象接口。type All id | name | age | email; type AfterExclude ExcludeAll, email; // id | name | ageExclude 的第二个参数可以是单个字符串也可以是联合类型表示要删除哪些。一个极易踩的坑Omit 第二个参数是“要删的键”不是“要保留的键”看这段代码interface User { id: number; name: string; age: number; email: string; } type UserKeys keyof User; // id | name | age | email type KeepKeys ExcludeUserKeys, email; // id | name | age type MyOmitUser OmitUser, KeepKeys;如果你以为KeepKeys是“保留的键”那就反了。OmitUser, KeepKeys会剔除id/name/age最后只剩下emailtype MyOmitUser { email: string };如果你想排除 email应该直接写type UserSafe OmitUser, email; // { id: number; name: string; age: number }记住Omit 是“不要”不是“只要”。要保留用 Pick要删除用 Omit。Partial所有字段可选PartialT把T的所有字段变成可选。type PartialUser PartialUser; const patchUser: PartialUser { name: m, age: 18 }; const emptyObj: PartialUser {}; // 合法场景更新接口的 patch body、表单草稿、部分配置合并。对象属性很多时Partial 能省去大量重复的?:。内部实现也很简单type PartialT { [P in keyof T]?: T[P]; };Record快速构造键值对RecordK, V用来构造“键类型为 K、值类型为 V”的对象。type Dict Recordstring, number; const obj: Dict { a: 1, b: 2 };最经典的场景是 HTTP 状态码映射type ErrorMsgMap Recordnumber, string; const errorMessage: ErrorMsgMap { 400: 请求参数错误, 401: 未登录请重新登录没有权限, 403: 权限不足禁止访问, 404: 资源不存在, 500: 服务器内部错误 }; function getErrMsg(code: number): string { return errorMessage[code] ?? 未知错误; }Recordnumber, string表示一个 key 是 number、value 是 string 的对象。相比{ [code: number]: string }Record 的语义更清晰也更适合作为工具类型的输出。ReturnType获取函数返回类型有时候你不想重复声明函数返回值的类型或者第三方库没有导出返回类型可以用ReturnType自动推导。function fn() { return { x: 1, y: 2 }; } type FnReturn ReturnTypetypeof fn; // { x: number; y: number }注意要用typeof fn获取函数类型而不是fn。ReturnType在 Redux action、Express handler、工具函数封装里很常见。一张表总结工具类型作用典型场景keyof T取所有键联合动态属性、遍历PickT, K挑选字段列表项、DTOOmitT, K剔除字段脱敏、排除 idExcludeU, K从联合类型中删除联合类型过滤PartialT所有字段可选更新、表单草稿RecordK, V构造键值对状态码、字典ReturnTypeF获取函数返回类型第三方库、封装写在最后工具类型不是炫技而是让类型像积木一样复用。你写得越多越会发现类型定义的成本不在“写”而在“改”工具类型能帮你把改动收敛到一处。下次在项目里遇到“这个类型跟那个类型就差几个字段”的时候先别急着新建interface想想 Pick、Omit、Partial 是不是已经够用了。
返回列表