TypeScript如何获取对象的非只读属性键 避免只读属性赋值报错
TypeScript 筛选非只读属性键实现
首先定义两个工具类型:
// 辅助工具:严格判断两个类型是否完全等价 type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B // 提取目标类型的所有非只读属性键 type WritableKeys<T> = { [K in keyof T]: IfEquals< { [P in K]: T[K] }, { -readonly [P in K]: T[K] }, K, never > }[keyof T]
实现逻辑
- 对类型
T的每一个属性键K,分别构造两个只有readonly修饰符差异的同结构类型 - 如果两个类型完全等价,说明原属性本身没有
readonly修饰,保留该键;否则说明是只读属性,过滤掉 - 最终返回所有非只读属性键的联合类型
业务代码改造
将你的代码中propertiesToClear的参数类型从Array<keyof Test>替换为Array<WritableKeys<Test>>即可:
interface Test { readonly a: string; b: string; } type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? A : B type WritableKeys<T> = { [K in keyof T]: IfEquals< { [P in K]: T[K] }, { -readonly [P in K]: T[K] }, K, never > }[keyof T] const clearProperties = (propertiesToClear: Array<WritableKeys<Test>>, onObject: Test): Test => { // 注意:原代码for in遍历数组拿到的是下标,需改为for of拿到属性名 for (const key of propertiesToClear) { onObject[key] = ""; // 不会再触发只读属性赋值报错 } return onObject; } // 调用测试 clearProperties(['b'], {a: 'test', b: 'test'}) // 编译正常 clearProperties(['a'], {a: 'test', b: 'test'}) // 编译直接报错:a 不属于允许的属性值
这个方案可以在调用阶段就拦截只读属性的传入,从类型层面完全规避赋值报错的问题。
内容的提问来源于stack exchange,提问作者OliverRadini
相关产品推荐
相关产品推荐

