包装泛型接口时如何保留属性的可选状态
问题:保留属性可选状态的Timestamped对象类型实现
需求
我需要实现ObjectWithTimestampedProperties<T>类型,将T中每个属性的类型转换为Timestamped<T[K]>,同时保留原属性的可选/必填状态。
当前的类型定义:
interface ITimestamp { timestamp: number; } type Timestamped<T> = { data: T } & ITimestamp; type ObjectWithTimestampedProperties<T extends object> = { [K in keyof T]: Timestamped<T[K]>; };
遇到的问题
在以下代码中,初始化空对象{}时触发类型错误:
class LocalStorageDataSource<T extends object> { public async load( keysToLoad: (keyof T)[] ): Promise<ObjectWithTimestampedProperties<Partial<T>>> { const data: ObjectWithTimestampedProperties<Partial<T>> = {}; // 此处报错 /** ... 业务逻辑代码 ... */ return data; } }
错误提示:Type '{}' is not assignable to type 'ObjectWithTimestampedProperties<Partial<T>>'.
我清楚问题所在:当前的类型定义[K in keyof T]: Timestamped<T[K]>会把所有属性转为必填,哪怕原属性是可选的,所以空对象无法满足所有必填属性的要求。但我不知道如何保留原属性的可选状态。
解决方案
要保留原属性的可选/必填状态,我们需要先区分T中的必填键和可选键,再分别进行类型转换:
步骤1:定义工具类型提取必填/可选键
// 提取T中的所有必填键 type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K }[keyof T]; // 提取T中的所有可选键 type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
步骤2:修改ObjectWithTimestampedProperties类型
type ObjectWithTimestampedProperties<T extends object> = { // 原必填属性 → 转换为必填的Timestamped类型 [K in RequiredKeys<T>]: Timestamped<T[K]>; } & { // 原可选属性 → 转换为可选的Timestamped类型 [K in OptionalKeys<T>]?: Timestamped<T[K]>; };
效果验证
当传入普通类型(含必填+可选属性)时:
interface User { id: number; name?: string; } type TimestampedUser = ObjectWithTimestampedProperties<User>; // 类型结构: // { // id: { data: number; timestamp: number }; // name?: { data: string; timestamp: number } | undefined; // }当传入
Partial<T>时:type PartialTimestampedUser = ObjectWithTimestampedProperties<Partial<User>>; // 类型结构: // { // id?: { data: number; timestamp: number } | undefined; // name?: { data: string; timestamp: number } | undefined; // }
此时,const data: ObjectWithTimestampedProperties<Partial<T>> = {};不再报错,因为所有属性都是可选的,空对象符合类型要求。
内容的提问来源于stack exchange,提问作者Catkinson
相关产品推荐
相关产品推荐

