You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 11:09:12