TypeScript泛型函数:如何实现按键匹配对应类型属性值的校验
如何实现键与对应属性值严格匹配的泛型函数
我需要编写一个泛型函数,接收两个参数:第一个是给定类型的键,第二个必须是该类型对应此键的属性值。比如给定类型:
type Example = { letter: 'a' | 'b' | 'c'; number: 1 | 2 | 3; }
希望以下调用是合法的:
myFunction<Example>('letter', 'a'); myFunction<Example>('letter', 'c'); myFunction<Example>('number', 1); myFunction<Example>('number', 2);
以下调用是非法的:
myFunction<Example>('letter', 1); myFunction<Example>('letter', 2); myFunction<Example>('number', 'b'); myFunction<Example>('number', 'c');
目前我的实现如下,但不符合需求:
type MyProperties = { size: 'small' | 'medium' | 'large'; type: 'a' | 'b' | 'c'; } /** * 第二个参数的类型不符合预期,因为即使传入的key是'size',它也会接受所有可能的值'small'、'medium'、'large'、'a'、'b'或'c'。 */ function testing<P>( key: keyof P, value: P[typeof key] ) { // } // 当前实现的效果: testing<MyProperties>('size', 'small'); // 符合预期 testing<MyProperties>('size', 'medium'); // 符合预期 testing<MyProperties>('size', 'large'); // 符合预期 testing<MyProperties>('size', 'a'); // 符合预期(不符合需求) testing<MyProperties>('size', 'b'); // 符合预期(不符合需求) testing<MyProperties>('size', 'c'); // 符合预期(不符合需求) testing<MyProperties>('size', 'something'); // 不符合预期 // 我期望的效果是: testing<MyProperties>('size', 'small'); // 符合预期 testing<MyProperties>('size', 'medium'); // 符合预期 testing<MyProperties>('size', 'large'); // 符合预期 testing<MyProperties>('size', 'a'); // 不符合预期:'a'不是'size'的有效值 testing<MyProperties>('size', 'b'); // 不符合预期:'b'不是'size'的有效值 testing<MyProperties>('size', 'c'); // 不符合预期:'c'不是'size'的有效值 testing<MyProperties>('size', 'something'); // 不符合预期:'something'不是'size'的有效值
请问这种需求是否可以实现?
可以实现,问题出在当前的泛型定义上——只声明泛型P时,key: keyof P是所有键的联合类型,P[typeof key]会被推断为P所有属性值的联合(即P[keyof P]),所以第二个参数会接受任意属性的有效值。
要解决这个问题,需要增加一个泛型参数K来约束具体的键,让TypeScript能够跟踪当前传入的是哪个键,从而准确推断对应属性值的类型:
type MyProperties = { size: 'small' | 'medium' | 'large'; type: 'a' | 'b' | 'c'; } function testing<P, K extends keyof P>(key: K, value: P[K]) { // 函数逻辑 } // 验证效果: testing<MyProperties, 'size'>('size', 'small'); // ✅ 合法 testing<MyProperties, 'size'>('size', 'medium'); // ✅ 合法 testing<MyProperties, 'size'>('size', 'large'); // ✅ 合法 testing<MyProperties, 'size'>('size', 'a'); // ❌ 非法:类型'"a"'不能赋值给类型'"small" | "medium" | "large"' testing<MyProperties, 'size'>('size', 'b'); // ❌ 非法:同上 testing<MyProperties, 'size'>('size', 'c'); // ❌ 非法:同上 testing<MyProperties, 'size'>('size', 'something'); // ❌ 非法:类型'"something"'不能赋值给类型'"small" | "medium" | "large"' testing<MyProperties, 'type'>('type', 'a'); // ✅ 合法 testing<MyProperties, 'type'>('type', 'b'); // ✅ 合法 testing<MyProperties, 'type'>('type', 'small'); // ❌ 非法:类型'"small"'不能赋值给类型'"a" | "b" | "c"'
甚至可以省略显式传入泛型参数,TypeScript会自动根据你传入的key推断出K的类型:
testing('size', 'small'); // ✅ 自动推断P=MyProperties,K='size' testing('type', 'b'); // ✅ 自动推断P=MyProperties,K='type' testing('size', 'a'); // ❌ 报错,符合预期
这种方式通过双重泛型绑定了键和对应属性值的关系,确保第二个参数只能是当前键对应的有效值,完全符合需求。
内容的提问来源于stack exchange,提问作者Rafael Berro
相关产品推荐
相关产品推荐

