如何在TypeScript中实现对象属性赋值的类型匹配校验?
TypeScript 实现属性值赋值的类型校验
我想要编写一个updateValue函数,该函数接收目标对象a、a的属性键aKey,以及对象b、b的属性键bKey,将b[bKey]的值赋值给a[aKey]。
尝试的初始代码如下:
const updateValue = <T1, T1Key extends keyof T1, T2, T2Key extends keyof T2>( a: T1, aKey: T1Key, b: T2, bKey: T2Key ) => { a[aKey] = b[bKey]; // 此处报错: // 'T1' could be instantiated with an arbitrary type which could be unrelated to 'T2'.ts(2322) }; const a = { a: 1, b: 'b', }; const b = { c: 2, d: 'd', };
预期合法的调用:
updateValue(a, 'a', b, 'c'); // 合理,a.a(number)和b.c(number)类型匹配
需要禁止的非法调用:
updateValue(a, 'b', b, 'c'); // 应该被禁止,a.b(string)和b.c(number)类型不匹配
初始代码仅校验属性是否存在,无法约束属性值的类型兼容性,以下是解决方案:
方法一:通过工具类型筛选兼容键
首先定义一个工具类型,筛选出对象中属性值可赋值给目标类型的键:
type AssignableKey<TObj, TTarget> = { [K in keyof TObj]: TObj[K] extends TTarget ? K : never; }[keyof TObj];
然后修改updateValue函数,用这个工具类型约束bKey的类型:
const updateValue = <T1, K1 extends keyof T1, T2>( a: T1, aKey: K1, b: T2, bKey: AssignableKey<T2, T1[K1]> ) => { a[aKey] = b[bKey]; };
这样修改后:
- 合法调用时,
T1[K1]为number,AssignableKey<typeof b, number>返回'c',参数类型匹配,调用正常。 - 非法调用时,
T1[K1]为string,AssignableKey<typeof b, string>仅返回'd',传入的'c'不在范围内,TypeScript会直接在调用处报错。
方法二:直接在泛型中添加类型约束
也可以不单独定义工具类型,直接在泛型参数中约束类型兼容性:
const updateValue = <T1, K1 extends keyof T1, T2, K2 extends keyof T2>( a: T1, aKey: K1, b: T2, bKey: K2 extends (T2[K2] extends T1[K1] ? K2 : never) ? K2 : never ) => { a[aKey] = b[bKey] as T1[K1]; };
这种写法同样能实现校验:当T2[K2]无法赋值给T1[K1]时,bKey的类型会被约束为never,调用时直接触发类型错误。
内容的提问来源于stack exchange,提问作者Roger Far
相关产品推荐
相关产品推荐

