TypeScript中给带:previous后缀的对象键赋值的类型问题
TypeScript中为带
:previous后缀的历史键赋值时的类型安全解决方案 问题场景
需要维护一个包含原值和对应历史值的对象,历史值的键以:previous为后缀(例如foo对应foo:previous)。尝试将原值赋值给对应历史键时,TypeScript抛出类型错误:Type "foo" is not assignable to type "${K}:previous",且需保留类型安全(避免所有历史值类型变为联合类型)。
最易理解的解决方法
通过显式类型断言将模板字符串生成的历史键关联到对应的泛型键类型,让TypeScript正确识别键与值的类型映射关系:
type Foo = { "foo": string; "bar": boolean } type PostfixKeys<T> = { [K in keyof T as `${string & K}:previous`]: T[K]; } type Foo2 = Foo & PostfixKeys<Foo> const test = <K extends keyof Foo>(key: K, foo: Foo2) => { // 显式断言历史键的类型为${K}:previous const previousKey = `${key}:previous` as `${K}:previous`; foo[previousKey] = foo[key]; // 类型检查通过,且保留类型安全 }
方案解释
TypeScript默认无法自动推断${key}:previous的类型就是Foo2中对应的${K}:previous键类型,通过显式断言previousKey为${K}:previous,可以让类型系统明确:
- 这个键属于
Foo2的键集合 - 该键对应的值类型与
foo[key]完全一致(即T[K])
验证类型安全
调用函数后,历史值的类型仍保持与原值一致:
const example: Foo2 = { foo: "hello", bar: true, "foo:previous": "", "bar:previous": false }; test("foo", example); const fooPrev = example["foo:previous"]; // 类型为string const barPrev = example["bar:previous"]; // 类型为boolean
为什么之前的方案不可行
使用T[keyof T]替代T[K]虽然能消除错误,但会丢失类型安全:所有历史键的类型都会变成string | boolean(Foo所有值类型的联合),无法区分foo:previous和bar:previous的具体类型。
内容的提问来源于stack exchange,提问作者chitzui
相关产品推荐
相关产品推荐

