You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

包装泛型接口时如何保留属性的可选状态

问题:保留属性可选状态的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 07:09:53