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

如何用TypeScript映射类型实现带readonly和可选属性且保留顺序?

TypeScript映射类型优化:根据定义自动应用readonly和可选属性

我需要在TypeScript中通过映射类型,根据初始类型定义自动为属性添加readonly和可选标记。先定义基础类型:

type PropertyAttributes = {
  readonly optional?: boolean;
  readonly readonly?: boolean;
  readonly value: string; // 示例简化,实际类型可能不同
};

type ObjectDefinition = Record<string, PropertyAttributes>;

目前我通过拆分四种不同类型再合并的方式实现需求,但代码冗长,且合并后无法保留原属性顺序:

// 对应四种属性组合的类型
type ReadonlyRequired<O extends ObjectDefinition> = {
  +readonly [K in keyof O as O[K]["readonly"] extends true
    ? O[K]["optional"] extends true
      ? never
      : K
    : K]: O[K];
};

type ReadonlyOptional<O extends ObjectDefinition> = {
  +readonly [K in keyof O as O[K]["readonly"] extends true
    ? O[K]["optional"] extends true
      ? K
      : never
    : never]?: O[K] | undefined;
};

type MutableRequired<O extends ObjectDefinition> = {
  -readonly [K in keyof O as O[K]["readonly"] extends true
    ? never
    : O[K]["optional"] extends true
    ? never
    : K]: O[K];
};

type MutableOptional<O extends ObjectDefinition> = {
  -readonly [K in keyof O as O[K]["readonly"] extends true
    ? never
    : O[K]["optional"] extends true
    ? K
    : never]?: O[K] | undefined;
};

// 展开工具类型
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

// 合并所有类型
type Combine<O extends ObjectDefinition> = Expand<
  ReadonlyRequired<O> &
    ReadonlyOptional<O> &
    MutableRequired<O> &
    MutableOptional<O>
>;

还有一个用于映射值的类型:

// 映射到value属性,保留键名
type MapToValue<O extends ObjectDefinition> = {[K in keyof O]: NonNullable<O[K]>["value"]}; // 用NonNullable避免可选类型导致的unknown

type ToObj<O extends ObjectDefinition> = MapToValue<Combine<O>>;

这个方案能正常工作,但代码太长,且属性顺序无法保留。想知道有没有更简洁的实现方式,或者可优化的细节。


优化方案:单映射类型实现

可以通过单个映射类型直接处理每个属性的readonly和可选标记,这样既简化代码,又能保留原属性顺序(因为同态映射会维持原键的顺序):

核心实现

// 直接根据每个属性的定义生成对应修饰符
type DefineObject<O extends ObjectDefinition> = {
  // 条件添加readonly:readonly为true时添加+readonly,否则-readonly
  [K in keyof O as K]: O[K]["readonly"] extends true 
    ? readonly (O[K]["optional"] extends true ? O[K] | undefined : O[K])
    : (O[K]["optional"] extends true ? O[K] | undefined : O[K]);
};

// 优化后的ToObj:直接映射到value并应用修饰符
type ToObj<O extends ObjectDefinition> = {
  [K in keyof O as K]: O[K]["readonly"] extends true 
    ? readonly (O[K]["optional"] extends true ? NonNullable<O[K]>["value"] | undefined : NonNullable<O[K]>["value"])
    : (O[K]["optional"] extends true ? NonNullable<O[K]>["value"] | undefined : NonNullable<O[K]>["value"]);
};

更简洁的版本(复用逻辑)

提取判断逻辑为单独工具类型,进一步提升可读性和复用性:

// 判断属性是否为可选
type IsOptional<T extends PropertyAttributes> = T["optional"] extends true ? true : false;
// 判断属性是否为只读
type IsReadonly<T extends PropertyAttributes> = T["readonly"] extends true ? true : false;

// 生成单个属性的最终类型(含修饰符)
type PropertyType<T extends PropertyAttributes> = IsReadonly<T> extends true
  ? readonly (IsOptional<T> extends true ? NonNullable<T>["value"] | undefined : NonNullable<T>["value"])
  : (IsOptional<T> extends true ? NonNullable<T>["value"] | undefined : NonNullable<T>["value"]);

// 最终目标类型
type ToObj<O extends ObjectDefinition> = {
  [K in keyof O]: PropertyType<O[K]>;
};

优化优势

  1. 代码精简:无需拆分多类型再合并,逻辑集中在单个映射中
  2. 保留属性顺序:同态映射维持ObjectDefinition中属性的原始顺序
  3. 可读性提升:逻辑分层清晰,工具类型可复用

内容的提问来源于stack exchange,提问作者Max

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 02:45:21