TypeScript如何约束对象两个属性键为同类型且支持自动推断
问题核心原因
直接给变量显式标注类型时,TypeScript 不支持「只指定部分泛型参数、自动推导剩余泛型」的能力。你最初写的双泛型Args<R, K>约束逻辑本身完全正确,但直接标注Args<Pizza>时,哪怕给K设置默认值K extends keyof R = keyof R,K也会直接取默认值keyof R,不会根据赋值的对象字面量收窄类型,自然无法实现foo和bar必须为同一个键的约束。
实现方案
你可以根据场景二选一,两种方案都不需要手动指定K泛型:
方案1:分布式条件类型(无需辅助函数,直接标注类型)
利用TS的分布式条件类型特性,遍历keyof R的所有取值,自动生成「foo和bar为同一个键」的所有可能结构的联合类型,使用时只需要传入R泛型即可:
type Args<R extends object> = keyof R extends infer K ? K extends keyof R ? { foo: K; bar: K } : never : never
使用示例:
type Pizza = { name: string radius: number // 注意TS没有int类型,数值类型统一用number标注 } // 校验通过:foo、bar为同一个键 const myArgs1: Args<Pizza> = { foo: 'name', bar: 'name' } const myArgs2: Args<Pizza> = { foo: 'radius', bar: 'radius' } // 类型报错:foo、bar为不同键,不符合联合类型的任何成员 const badArgs: Args<Pizza> = { foo: 'name', bar: 'radius' }
该方案优点是无需额外编写辅助函数,直接标注类型即可使用;缺点是如果R的属性数量极多(上百个),会生成等量的联合类型成员,存在极微小的类型检查性能开销,日常业务场景完全感知不到。
方案2:泛型辅助函数(性能最优,适合大类型场景)
保留双泛型的类型定义,通过泛型工厂函数触发TS的自动推导能力:使用时只需要指定R的类型,K会根据传入的对象字面量自动收窄,不需要手动传入:
// 给K设置默认值兜底,避免未传K时的类型报错 type Args<R extends object, K extends keyof R = keyof R> = { foo: K bar: K } // 外层泛型传入目标记录类型R,内层泛型自动推导K function defineArgs<R extends object>() { return <K extends keyof R>(args: Args<R, K>) => args }
使用示例:
type Pizza = { name: string radius: number } // 只需要指定一次R类型,可复用生成多个校验通过的Args对象 const definePizzaArgs = defineArgs<Pizza>() // 校验通过:自动推导K为对应字面量类型 const myArgs1 = definePizzaArgs({ foo: 'name', bar: 'name' }) const myArgs2 = definePizzaArgs({ foo: 'radius', bar: 'radius' }) // 类型报错:两个键类型不匹配 const badArgs = definePizzaArgs({ foo: 'name', bar: 'radius' })
该方案优点是类型定义简洁,不会生成大量联合类型,性能更好;缺点是需要额外编写一个辅助工厂函数。
内容的提问来源于stack exchange,提问作者Sasgorilla
相关产品推荐
相关产品推荐

