如何为TypeScript递归生成类型基于required字段添加可选属性
如何在递归TypeScript类型中根据required字段推断可选属性
问题分析
你当前的GenerateItems类型可根据数组元素生成对应字段类型,但无法依据required标记区分属性的可选性。需要将映射类型拆分为必填和可选两部分,再通过交叉类型合并结果。
解决方案
重构GenerateItems类型,分别处理必填属性与可选属性,最后交叉合并:
type ExpandRecursively<T> = T extends object ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never : T; // 抽离字段类型判断逻辑,避免重复代码 type FieldType<T> = T extends { subFields: infer S extends readonly any[] } ? GenerateItems<S>[] : T extends { type: infer Type extends string } ? Type extends "string" ? string : Type extends "url" ? string : Type extends "file" ? T extends { allowedFileTypes: infer FT extends readonly string[] } ? FT[number] : never : never : never; type GenerateItems<T extends readonly any[]> = // 处理必填属性:无required或required为true的字段 { [K in keyof T & `${bigint}` as T[K] extends { name: infer N extends string } ? (T[K] extends { required: false } ? never : N) : never ]: FieldType<T[K]> } & // 处理可选属性:required明确为false的字段 { [K in keyof T & `${bigint}` as T[K] extends { name: infer N extends string } ? (T[K] extends { required: false } ? N : never) : never ]?: FieldType<T[K]> }; // 测试示例 const items = [ { name: "label", type: "string", defaultValue: "", }, { name: "url", type: "url", defaultValue: "", required: false, }, ] as const; type Items = ExpandRecursively<GenerateItems<typeof items>>; // 生成的类型等价于: // { // label: string; // url?: string; // }
关键说明
- 拆分映射逻辑:将原单一映射拆分为两个部分,分别处理必填和可选属性,通过交叉类型
&合并最终结果。 - 属性筛选规则:
- 必填属性:筛选
required不为false的元素(包括未定义required的默认情况),生成不带?的必填属性。 - 可选属性:筛选
required明确为false的元素,生成带?的可选属性。
- 必填属性:筛选
- 代码复用:将字段类型的判断逻辑抽离为
FieldType,避免重复编写递归和类型判断代码。 - 递归兼容:保留原有的
subFields递归处理逻辑,嵌套字段同样能正确推断可选性。
内容的提问来源于stack exchange,提问作者pilotguy
相关产品推荐
相关产品推荐

