如何递归泛型转换{ a: A | undefined }为{ a?: A }?
解决方案:递归转换可选属性类型并清理undefined字段
类型层面:定义递归泛型转换类型
首先创建一个泛型类型UndefinedToOptional,它会递归遍历目标类型的所有字段,将类型为X | undefined的属性转换为可选属性?: X,同时处理嵌套的对象和数组:
type UndefinedToOptional<T> = // 处理null/undefined原始类型 T extends null | undefined ? T : // 处理数组,递归转换数组元素类型 T extends Array<infer U> ? Array<UndefinedToOptional<U>> : // 处理对象类型 T extends object ? { [K in keyof T as T[K] extends undefined ? never : K]?: UndefinedToOptional<Exclude<T[K], undefined>> } : // 基础类型直接返回 T;
运行时:递归清理undefined属性
由于开启exactOptionalPropertyTypes: true后,{ a: undefined }和{ }结构不等价,且schema库会做运行时验证,必须手动移除所有值为undefined的属性。以下是对应的递归清理函数:
function cleanUndefinedProperties<T>(obj: T): UndefinedToOptional<T> { // 处理null/undefined if (obj === null || obj === undefined) return obj as UndefinedToOptional<T>; // 处理数组,递归清理每个元素 if (Array.isArray(obj)) { return obj.map(item => cleanUndefinedProperties(item)) as UndefinedToOptional<T>; } // 处理对象,遍历并清理每个属性 if (typeof obj === 'object') { const cleaned = {} as UndefinedToOptional<T>; for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const value = obj[key]; const cleanedValue = cleanUndefinedProperties(value); // 仅保留非undefined的属性值 if (cleanedValue !== undefined) { (cleaned as Record<string, unknown>)[key] = cleanedValue; } } } return cleaned; } // 基础类型直接返回 return obj as UndefinedToOptional<T>; }
使用示例
假设你有如下源类型和对象:
// 源类型:部分字段为X | undefined type SourceData = { name: string | undefined; profile: { age: number | undefined; tags: Array<{ label: string | undefined }>; }; }; // 转换后的目标类型:对应字段变为可选属性 type TargetData = UndefinedToOptional<SourceData>; // TargetData 等价于: // { // name?: string; // profile?: { // age?: number; // tags?: Array<{ label?: string }>; // }; // }
处理源对象:
const rawData: SourceData = { name: undefined, profile: { age: undefined, tags: [{ label: undefined }, { label: "typescript" }] } }; const processedData = cleanUndefinedProperties(rawData); // processedData 的实际结构: // { // profile: { // tags: [{}, { label: "typescript" }] // } // } // 既符合TargetData的类型要求,也能通过schema库的运行时验证
内容的提问来源于stack exchange,提问作者Toby Hobson
相关产品推荐
相关产品推荐

