TypeScript泛型函数如何约束Record类型并保留输出类型?
如何在TypeScript中约束泛型函数输入的同时保留输出具体类型
要解决这个问题,核心是既要限制输入类型不能包含非数字键,又要让泛型参数T保留输入的具体结构。原代码的问题在于T extends Record<number, boolean>的约束是宽松的——TypeScript的结构子类型允许T包含额外属性,只要它满足Record<number, boolean>的要求即可。
解决方案1:交叉类型约束额外属性为never
通过交叉一个Record<Exclude<keyof T, number>, never>类型,强制所有非数字键的属性值必须为never(即不能存在有效取值),从而在传入非法键时报错,同时保留T的具体类型:
const func = <T extends Record<number, boolean>>(input: T & Record<Exclude<keyof T, number>, never>): T => input; // 合法输入:无报错,x的类型为 { 1: true } const x = func({ 1: true }); // 非法输入:"test"属性值为false,与never类型冲突,触发报错 const y = func({ 1: true, test: false });
解决方案2:用条件类型限制键类型
通过在参数类型中添加keyof T extends number ? T : never的条件判断,直接拒绝包含非数字键的输入,同时保留T的具体结构:
const func = <T extends Record<number, boolean>>(input: keyof T extends number ? T : never): T => input; // 合法输入:x的类型为 { 1: true } const x = func({ 1: true }); // 非法输入:传入包含string键的对象时,参数类型变为never,触发报错 const y = func({ 1: true, test: false });
解决方案3:TypeScript 4.9+ 使用where子句(更直观)
如果你的项目使用TypeScript 4.9及以上版本,可以用where子句直接约束keyof T必须是number类型,写法更清晰:
const func = <T extends Record<number, boolean> where keyof T extends number>(input: T): T => input; // 合法输入:x的类型为 { 1: true } const x = func({ 1: true }); // 非法输入:触发报错 const y = func({ 1: true, test: false });
适配复杂场景
这些方案都不会破坏T的泛型特性,你可以继续将T用于其他复杂的返回类型或逻辑中,同时保证输入的类型合法性。
内容的提问来源于stack exchange,提问作者Mikkel R. Lund
相关产品推荐
相关产品推荐

