TypeScript异构对象静态插入新字段(不覆盖旧键)实现求助
编译时安全地为异构对象添加新字段
问题描述
需要在编译阶段确认对象中不存在指定键的前提下,为异构对象添加新字段,以此跟踪已初始化资源及其类型,实现类型安全的未初始化资源访问防护,同时在静态层面掌握所有已初始化资源。
理想的函数定义如下:
type FreshKey<A,K> = K extends keyof A ? never : K const f <A, K extends string, V> = (x: A) => (k: FreshKey<A,K>, v: V) => A & { [k: K]: V} = ...
使用示例:
const z = {} const y = f(z)("foo", []); const x = f(y)("bar", 3); const v = f(x)("baz", ""); const v2 = f(x)("foo", []) // 此处应无法通过类型检查,因"foo"已存在
遇到的问题:无法写出TypeScript认可的函数返回类型,触发TS1337错误(索引签名参数类型不能是字面量类型或泛型类型)。优先实现无类型断言的合法类型定义,若无法实现,使用类型断言也可接受。
解决方案
方案1:利用Record工具类型规避索引签名限制
将返回类型中的自定义索引签名替换为TypeScript内置的Record工具类型,既符合语法规范,又能保留类型安全:
type FreshKey<A, K> = K extends keyof A ? never : K; const f = <A, K extends string, V>(x: A) => (k: FreshKey<A, K>, v: V): A & Record<K, V> => Object.assign({}, x, { [k]: v });
方案2:类型断言绕过语法错误(退而求其次)
如果坚持使用原索引签名的写法,可以通过类型断言告知TypeScript忽略语法检查:
type FreshKey<A, K> = K extends keyof A ? never : K; const f = <A, K extends string, V>(x: A) => (k: FreshKey<A, K>, v: V) => Object.assign({}, x, { [k]: v }) as A & { [P in K]: V };
效果验证
两种方案都能实现预期需求:
- 新增不存在的键时,类型检查通过,返回对象会自动包含新键的类型信息
- 尝试添加已存在的键时,
FreshKey类型会推导为never,直接触发类型错误,阻止非法操作
比如示例中的const v2 = f(x)("foo", []),TypeScript会提示参数类型不匹配,因为"foo"属于x的键集合,FreshKey<typeof x, "foo">等于never,无法传入字符串"foo"。
内容的提问来源于stack exchange,提问作者RattlesnakeSuitcase
相关产品推荐
相关产品推荐

