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

TypeScript中如何约束对象属性值为该对象自身的键名

问题描述

假设我定义了如下对象:

const obj: Obj = {
    a: ['b'],
    b: ['c']
}

需要实现以下类型校验规则:

  • a: ['b'] 写法符合校验规则,因为b是当前对象的自有键
  • b: ['c'] 写法需要被判定为类型错误,因为c不属于当前对象的自有键

目前尝试编写的类型定义如下:

type Obj = {
    [key: string]: (keyof Obj)[]
}

使用该类型定义后,两处属性赋值都会抛出如下类型错误:

Type 'string' is not assignable to type 'never'

解决方案

出现never报错的核心原因是你写的Obj类型存在递归自引用,且没有固定的已知键,TypeScript推导时会认为Obj不存在任何确定属性,因此keyof Obj会被推导为never,自然无法赋值任何字符串值。

要实现「数组元素必须为当前对象自身存在的键」的自引用约束,不能直接靠固定的索引签名实现,需要借助泛型辅助函数做动态类型推导:

const createObj = <T extends Record<keyof T, (keyof T)[]>>(obj: T): T => obj;

// 校验通过,符合预期
const obj = createObj({
  a: ['b']
})

// 抛出类型错误:Type '"c"' is not assignable to type '"a" | "b"',符合预期
const badObj = createObj({
  a: ['b'],
  b: ['c']
})

实现原理

  • 泛型参数T会在你传入对象字面量时,自动推导出对象的实际结构,此时keyof T可以准确拿到当前对象所有真实存在的键,不会出现递归索引签名推导为never的问题
  • 泛型约束T extends Record<keyof T, (keyof T)[]>会强制要求对象的每个属性值,都必须是由对象自身键组成的数组,自然可以拦截到不存在的键c
  • 辅助函数返回值的类型和直接书写对象字面量的类型完全一致,不会丢失任何属性类型信息

如果需要单独抽离Obj类型,可以配合泛型改写,不影响校验逻辑:

type Obj<T> = {
  [K in keyof T]: (keyof T)[]
}
const createObj = <T extends Obj<T>>(obj: T): T => obj;

内容的提问来源于stack exchange,提问作者Andy Jessop

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 10:51:22