TypeScript类型重映射时如何保留可选属性模式?
解决TypeScript类型重映射时保留可选属性的问题
问题分析
你的PrefixAll类型工具在给接口属性添加前缀时,会丢失原属性的可选性——所有属性都变成了必填项。这是因为TypeScript使用as进行键重映射时,不会自动将原属性的可选修饰符(?)关联到新生成的属性上,必须手动处理才能保留原有属性的可选状态。
解决方案
我们可以通过分离必填/可选属性分别处理,再合并结果的方式解决这个问题:
- 先定义工具类型提取接口中的所有可选属性键
- 修改
PrefixAll类型,分别对必填、可选属性添加前缀,最后合并两种结果
完整代码实现
// 提取接口中的可选属性键 type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never; }[keyof T]; /** 为属性添加前缀并大写属性首字母,同时保留原属性的可选状态 */ type PrefixAll<T, P extends string> = // 处理必填属性:保持必填状态 { [K in Exclude<keyof T, OptionalKeys<T>> & string as `${P}${Capitalize<K>}`]: T[K] } & // 处理可选属性:保留可选状态 { [K in OptionalKeys<T> & string as `${P}${Capitalize<K>}`]?: T[K] };
使用示例
import type { ReactNode, CSSProperties } from 'react'; interface ChildrenProps { children: ReactNode; className?: string; style?: CSSProperties; } // 生成带前缀的新类型 type ChildrenPropsWithPrefix = PrefixAll<ChildrenProps, 'child'>;
生成的ChildrenPropsWithPrefix类型与期望完全一致:
interface ChildrenPropsWithPrefix { childChildren: ReactNode; childClassName?: string; childStyle?: CSSProperties; }
原理说明
OptionalKeys<T>:通过Pick<T, K>判断单个属性是否可选——如果空对象{}可以赋值给Pick<T, K>,说明该属性是可选的(可选属性允许未定义)。PrefixAll类型中,先用Exclude分离出必填属性,直接生成带前缀的必填属性;再对可选属性生成带前缀的可选属性,最后通过交叉类型合并两者,既满足前缀和首字母大写的要求,也完整保留了原属性的可选状态。
内容的提问来源于stack exchange,提问作者97z4moon
相关产品推荐
相关产品推荐

