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

如何让TypeScript类型中的特定嵌套属性变为必填?

实现支持嵌套属性的PartialExcept类型助手

你可以通过定义一个支持嵌套路径指定的类型工具NestedPartialExcept来统一处理多层级的必填属性设置,替代手动嵌套PartialExcept和Required的繁琐写法。以下是完整实现和用法示例:

第一步:定义辅助类型(处理嵌套路径与递归转换)

首先实现几个基础辅助类型,用来解析嵌套属性路径并递归修改类型:

// 提取所有可能的嵌套属性路径(支持数组)
type NestedKeys<T> = T extends Array<infer U>
  ? `${number}.${NestedKeys<U>}` | NestedKeys<U>
  : T extends object
  ? { [K in keyof T]: K extends string ? `${K}.${NestedKeys<T[K]>}` | K : never }[keyof T]
  : never;

// 递归将指定路径的属性设为必填,其余保持Partial
type NestedRequired<T, K extends string> = T extends Array<infer U>
  ? Array<NestedRequired<U, K extends `${number}.${infer Rest}` ? Rest : K>>
  : T extends object
  ? {
      [P in keyof T]: P extends K
        ? Required<T[P]>
        : K extends `${P}.${infer Rest}`
        ? NestedRequired<T[P], Rest>
        : Partial<T[P]>;
    } & Required<Pick<T, Extract<keyof T, K>>>
  : T;

// 最终的NestedPartialExcept类型:指定嵌套路径为必填,其余全为Partial
type NestedPartialExcept<T, K extends NestedKeys<T>> = NestedRequired<Partial<T>, K>;

第二步:使用示例

用你提供的业务类型来测试:

type Team = {
  name?: string
  description?: string
  goals?: Goal[]
  metadata?: Metadata
}

type Metadata = {
  time?: string
  period?: number
  shots?: number
}

type Goal = {
  scorer?: string
  number?: string
}

// 使用NestedPartialExcept统一指定所有嵌套必填属性
type Game = NestedPartialExcept<{ home: Team }, 
  "home.name" | "home.description" | "home.goals[].scorer" | "home.goals[].number" | "home.metadata.time" | "home.metadata.period"
>;

这样定义的Game类型和你原来手动拼接的类型完全等价,但写法更统一简洁,所有必填属性都集中在一个联合类型中指定。

类型说明

  • NestedKeys<T>:自动生成类型T的所有嵌套属性路径,比如对于{ home: Team },会生成"home"、"home.name"、"home.goals[].scorer"等路径。
  • NestedRequired<T, K>:递归遍历类型T,将所有匹配路径K的属性设为Required,其余属性保持Partial。
  • NestedPartialExcept<T, K>:基于Partial<T>进行转换,确保未指定的属性都是可选的,仅指定路径的属性为必填。

内容的提问来源于stack exchange,提问作者Amrit Kahlon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 18:59:54