关于TypeScript中更简洁修改嵌套属性必填/存在状态的方案咨询
TypeScript中更简洁修改嵌套属性必填/存在状态的方案咨询
我太懂这种感受了——当你面对一堆深层嵌套的JSON结构类型,每次要调整某个嵌套属性的必填状态或者移除它,都得套好几层Omit、Pick和Required,写出来的代码不仅冗长,嵌套层级一深,维护起来简直是噩梦。你当前的写法确实能解决问题,但对于复杂场景来说,确实不够高效。
现有更简洁的解决方案:自定义深层工具类型
我们可以自己实现一个支持路径指定、批量修改、兼容数组的自定义工具类型,一次性搞定多个嵌套属性的状态调整,不用再层层嵌套Omit和Pick。
比如下面这个实现,支持三种操作:必填化(required)、可选化(optional)、移除属性(omit),还能自动处理数组里的对象元素:
// 辅助类型:拆分路径字符串,比如把"properties.b"拆分成["properties", "b"] type SplitPath<T extends string> = T extends `${infer Head}.${infer Tail}` ? [Head, ...SplitPath<Tail>] : [T]; // 核心工具:针对单个路径修改属性状态 type DeepModifySingle<T, Path extends string, Op extends "required" | "optional" | "omit"> = // 先处理数组:如果目标是数组,递归修改数组元素 T extends Array<infer Item> ? Array<DeepModifySingle<Item, Path, Op>> : // 处理对象类型 T extends object ? SplitPath<Path> extends [infer FirstKey, ...infer RestPath] ? FirstKey extends keyof T ? RestPath extends [] ? // 路径走到最后一级,执行对应操作 Op extends "required" ? Omit<T, FirstKey> & Required<Pick<T, FirstKey>> : Op extends "optional" ? Omit<T, FirstKey> & Partial<Pick<T, FirstKey>> : Op extends "omit" ? Omit<T, FirstKey> : T : // 还有嵌套路径,递归处理下一级 Omit<T, FirstKey> & { [K in FirstKey]: DeepModifySingle<T[K], RestPath[number], Op>; } : T : T : T; // 批量处理多个路径的工具类型 type DeepModifyMany<T, Mods extends Record<string, "required" | "optional" | "omit">> = // 递归处理每个属性,匹配要修改的路径 T extends object ? { [Key in keyof T]: // 检查当前Key是否是某个修改路径的前缀 { [Path in keyof Mods]: Path extends `${Key}.${infer SubPath}` ? SubPath : never } extends infer SubMods ? // 提取当前Key对应的子路径修改规则 SubMods extends Record<string, never> ? // 没有子路径规则,检查当前Key是否是直接要修改的路径 Key extends keyof Mods ? Mods[Key] extends "required" ? Required<T[Key]> : Mods[Key] extends "optional" ? Partial<T[Key]> : Mods[Key] extends "omit" ? never : T[Key] : DeepModifyMany<T[Key], Mods> : DeepModifyMany<T[Key], SubMods> : T[Key]; } : T;
怎么用这个工具类型?
针对你给出的第一个例子,只需要一行就能完成v2版本的类型定义:
type SomeObject = { id: string; description: string; properties: { a?: string; // v1必填,v2移除 b?: string; // v2必填 otherSharedProperties: string; }; }; // 批量指定修改规则:移除properties.a,必填properties.b type SomeObjectForV2 = DeepModifyMany<SomeObject, { "properties.a": "omit", "properties.b": "required" }>;
如果是数组嵌套的场景,这个工具也能自动处理数组里的对象:
type SomeObjectWithArray = { id: string; description: string; properties: Array<{ a?: string; b?: string; otherSharedProperties: string; }>; }; // 调整数组元素里的属性状态 type SomeObjectWithArrayForV2 = DeepModifyMany<SomeObjectWithArray, { "properties.a": "omit", "properties.b": "required" }>;
有没有官方提案或第三方支持?
目前TypeScript官方还没有内置这种针对指定路径的深层属性修改工具类型,但社区里已经有很多讨论和成熟的解决方案:
- 第三方库比如
type-fest提供了DeepSetRequired、DeepSetOptional、DeepOmit等工具,支持路径指定,直接引入就能用,不用自己实现; - TypeScript的GitHub仓库里有不少相关提案,比如针对深层属性操作的统一工具类型讨论,不过目前还没有正式纳入标准。
总结
对于你这种只处理JSON结构(不需要函数、泛型等复杂场景)的需求,自定义的DeepModifyMany完全能满足,写法比你当前的组合式调用简洁太多,还能批量处理多个属性。如果不想自己维护类型实现,直接用社区成熟库也是不错的选择,官方原生支持的话可能还要等一段时间,但相关讨论一直在推进。
内容来源于stack exchange
相关产品推荐
相关产品推荐

