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

创建新对象时keyof T类型信息丢失的原因及疑问

TypeScript 索引类型赋值错误解析:为什么{ [key]: "ho" }不匹配预期类型?

问题场景

我在封装mongoose查询的抽象函数时遇到类型问题,简化后的场景如下:

function x<T extends object, K extends keyof T> (
  obj: T,
  key: K
) {
  // Type '{ [x: string]: string; }' is not assignable to
  // type '{ [Q in K]: string; }'.(2322)
  const y: {[P in K]: {[Q in P]: string}}[K] = { [key]: "ho" };
}

明明已明确满足类型约束,为何会出现此类型错误?TypeScript为何将key的类型判定为string,它也可能是number啊?


错误原因解析

1. 动态属性名的类型推断规则

当使用{ [key]: "ho" }这种动态属性名创建对象时,TypeScript会默认将索引类型推断为string。这是因为JavaScript的底层机制:无论传入的key是number还是symbol类型,最终都会被转换为字符串(比如{ [123]: "test" }实际等价于{ "123": "test" })。因此TypeScript在这里会把对象类型推断为{ [x: string]: string },而非你期望的精确属性类型。

2. 精确类型与索引签名类型的兼容性问题

你定义的预期类型{[P in K]: {[Q in P]: string}}[K]展开后等价于{ [Q in K]: string },这是一个仅包含K类型属性的精确对象类型。但推断出的{ [x: string]: string }是字符串索引签名类型,允许任意字符串属性。在TypeScript的类型系统中,精确类型无法兼容更宽泛的索引签名类型——因为后者可能包含超出K范围的额外属性,违反了类型约束。


解决方案

方案1:类型断言直接指定类型

通过类型断言告诉TypeScript,当前对象就是你期望的精确类型:

function x<T extends object, K extends keyof T> (
  obj: T,
  key: K
) {
  const y: {[P in K]: {[Q in P]: string}}[K] = { [key]: "ho" } as {[Q in K]: string};
}

方案2:使用Object.fromEntries生成精确类型

Object.fromEntries可以根据键值对数组生成对象,配合类型断言能更精准地约束类型:

function x<T extends object, K extends keyof T> (
  obj: T,
  key: K
) {
  const y: {[P in K]: {[Q in P]: string}}[K] = Object.fromEntries([[key, "ho"]]) as {[Q in K]: string};
}

方案3:封装工具函数辅助类型推断

创建一个泛型工具函数,确保返回的是包含指定属性的精确对象类型:

function createExactProp<K extends PropertyKey>(key: K, value: string): { [Q in K]: string } {
  return { [key]: value } as { [Q in K]: string };
}

function x<T extends object, K extends keyof T> (
  obj: T,
  key: K
) {
  const y: {[P in K]: {[Q in P]: string}}[K] = createExactProp(key, "ho");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 08:19:57