如何在TypeScript中将可选字段转为必填且保留原类型?
问题描述
我定义了如下类型:
type SomeType = { hello?: string | undefined; world: number; meow?: string; };
希望将其转换为:
type SomeTypeExplicit = { hello: string | undefined; world: number; meow: string; };
我的需求是通过这个转换后的类型,在组件中强制“所有可选属性必须有默认值”的规则——即使默认值是undefined。
但现有方案都无法满足需求:
- 使用
Required<SomeType>会得到过于严格的结果,直接移除了undefined类型:type SomeTypeRequired = { hello: string; world: number; meow: string; }; - 使用“Required but possibly undefined type”的方案又过于宽松,给所有原本没有
undefined的可选属性也加上了undefined:type SomeTypeLooseRequired = { hello: string | undefined; world: number; meow: string | undefined; };
请问如何创建泛型实现从SomeType到SomeTypeExplicit的转换?
解决方案
在TypeScript中,可选属性的类型会自动包含undefined,所以meow?: string和meow?: string | undefined在类型系统中是等价的。要实现你的需求,需要区分“允许undefined的可选属性”和“不允许undefined的可选属性”,以下是两种可行方案:
方案一:手动指定保留undefined的属性
创建一个泛型,接受原始类型和需要保留undefined的属性列表,对指定属性保留原有类型,对其他可选属性移除undefined并转为必选:
type ExplicitRequired<T, KeepUndefined extends keyof T = never> = { [K in keyof T]-?: K extends KeepUndefined ? T[K] : Exclude<T[K], undefined>; };
使用示例
type SomeType = { hello?: string | undefined; world: number; meow?: string; }; // 指定'hello'保留undefined,其他可选属性转为必选且移除undefined type SomeTypeExplicit = ExplicitRequired<SomeType, 'hello'>;
转换后的类型完全符合需求:
type SomeTypeExplicit = { hello: string | undefined; world: number; meow: string; };
方案二:显式标记需要保留undefined的属性
如果你希望自动推断,可以通过辅助类型标记需要保留undefined的属性,让泛型识别这些标记并做对应处理:
// 辅助类型,用于标记需要保留undefined的属性 type MarkUndefined<T> = T | undefined; // 原始类型中用MarkUndefined标记需要保留undefined的属性 type SomeType = { hello?: MarkUndefined<string>; world: number; meow?: string; }; // 判断属性类型是否是标记过的MarkUndefined type IsMarkedUndefined<T> = T extends MarkUndefined<infer U> ? true : false; // 实现转换逻辑的泛型 type ExplicitRequired<T> = { [K in keyof T]-?: IsMarkedUndefined<T[K]> extends true ? T[K] : Exclude<T[K], undefined>; }; // 转换得到目标类型 type SomeTypeExplicit = ExplicitRequired<SomeType>;
这种方式不需要手动指定属性列表,但需要你在原始类型中用MarkUndefined显式标记允许undefined的可选属性。
内容的提问来源于stack exchange,提问作者Terbiy
相关产品推荐
相关产品推荐

