You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 08:13:03