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

如何泛型地将TypeScript Map转换为Readonly Map?

实现包含泛型Map的类的只读副本

要实现类的只读副本,同时把内部的Map转为ReadonlyMap并保留泛型类型,TypeScript完全支持这个需求。核心是自定义一个深度只读工具类型,专门处理Map到ReadonlyMap的转换,同时递归处理其他属性。

步骤1:自定义深度只读工具类型

用条件类型判断目标类型,如果是Map<K, V>就转为对应的ReadonlyMap<K, V>,否则递归处理对象属性或直接返回原始类型:

type DeepReadonly<T> = 
  // 处理Map,提取泛型K和V,转为ReadonlyMap
  T extends Map<infer K, infer V> ? ReadonlyMap<K, DeepReadonly<V>> :
  // 处理普通对象,递归转为只读属性
  T extends object ? { readonly [P in keyof T]: DeepReadonly<T[P]> } :
  // 原始类型直接返回
  T;

步骤2:在类中实现copy()方法

在你的类里,让copy()方法返回DeepReadonly<你的类>类型的实例。运行时只需返回一个副本(类型断言即可,因为运行时行为不重要):

class DataStore<K, V> {
  private content: Map<K, V>;
  public label: string;

  constructor(label: string, initialData: Map<K, V>) {
    this.label = label;
    this.content = new Map(initialData);
  }

  // 返回深度只读的副本
  copy(): DeepReadonly<DataStore<K, V>> {
    return new DataStore(this.label, this.content) as DeepReadonly<DataStore<K, V>>;
  }

  // 示例修改方法
  addEntry(key: K, value: V) {
    this.content.set(key, value);
  }
}

效果验证

使用时,只读副本的属性和内部Map都会被TypeScript标记为只读,触发编译错误:

const store = new DataStore("demo", new Map([["id", 123]]));
const readonlyStore = store.copy();

// ❌ 编译错误:label是只读属性
readonlyStore.label = "new label";

// ❌ 编译错误:ReadonlyMap没有set方法
readonlyStore.content.set("name", "Alice");

// ✅ 可以正常读取
console.log(readonlyStore.content.get("id"));

进阶:隐藏修改方法(可选)

如果希望只读副本完全暴露只读操作,可以定义一个只读接口,让copy()返回该接口类型:

interface ReadonlyDataStore<K, V> {
  readonly label: string;
  readonly content: ReadonlyMap<K, V>;
  getEntry(key: K): V | undefined;
}

class DataStore<K, V> implements ReadonlyDataStore<K, V> {
  private content: Map<K, V>;
  public readonly label: string;

  constructor(label: string, initialData: Map<K, V>) {
    this.label = label;
    this.content = new Map(initialData);
  }

  copy(): ReadonlyDataStore<K, V> {
    return new DataStore(this.label, this.content);
  }

  getEntry(key: K): V | undefined {
    return this.content.get(key);
  }

  addEntry(key: K, value: V) {
    this.content.set(key, value);
  }
}

此时readonlyStore只能调用getEntry,无法访问addEntry,进一步强化不可变性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:05:28