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
相关产品推荐
相关产品推荐

