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

TypeScript如何创建只读键仅可设为特定值的映射类型

TypeScript 实现自定义规则的通用映射类型

可行实现方案

要实现需求,你需要先解决两个核心问题:

  • 可靠判断原类型中单个属性是否为只读
  • 构建新类型时移除所有属性的只读修饰符(否则原只读属性在新类型中依然不可写,无法赋值"FLAG"),再按属性只读性匹配对应值类型

1. 只读属性判断工具类型

TypeScript 没有内置的只读属性判断工具,可以利用条件类型的修饰符兼容性差异实现稳定判断:

type IsReadonly<T, K extends keyof T> =
  (<U>() => U extends { -readonly [Q in K]: T[Q] } ? 1 : 2) extends
  (<U>() => U extends { [Q in K]: T[Q] } ? 1 : 2)
  ? false
  : true;

判断逻辑:如果属性K本身不是只读的,移除只读修饰符后的类型和原类型完全等价,两个条件类型返回值一致;如果属性是只读的,二者存在修饰符差异,返回值不同。

2. 目标映射类型实现

映射时统一移除所有属性的只读修饰符、设置为可选属性,再通过IsReadonly判断值类型规则:

type KindOfMutable<T> = {
  -readonly [P in keyof T]?: IsReadonly<T, P> extends true ? "FLAG" : T[P] | "FLAG";
};

原写法失效原因

你之前的联合类型写法存在两个本质问题:

  • 联合类型的属性修饰符会取所有联合成员的交集:第二个联合分支保留了原类型的readonly v属性,因此整个联合类型的v属性依然被标记为只读,赋值操作自然会报错
  • 联合类型只能做整体结构的拼接,无法实现单个属性粒度的规则区分:要么所有属性都只能赋值"FLAG",要么所有属性都可以赋值原值/"FLAG",达不到「只读键仅允许FLAG、可写键允许原值/FLAG」的要求

效果验证

使用你提供的示例测试,完全符合预期:

interface thingy {
  x: number;
  readonly v: number;
}
const thing1: KindOfMutable<thingy> = {x: 1};
// 正常赋值,无类型错误
thing1.v = "FLAG";

// 以下场景会正常抛出类型错误
thing1.v = 123; // 原只读属性v仅允许赋值"FLAG",不允许赋值number
thing1.x = true; // 原可写属性x仅允许赋值number | "FLAG",不允许赋值boolean

// 以下场景符合类型规则,无错误
thing1.x = 456;
thing1.x = "FLAG";

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 11:33:11