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

如何解决TypeScript未基于输入对象键约束函数返回属性的问题?

问题原因

你遇到的问题核心在于metadataKeysMapper的类型声明太宽泛(Record<string, MetadataKeys>),导致TypeScript无法识别具体的键到枚举成员的映射关系,进而使得MetadataResponse<T>类型错误地包含了所有MetadataKeys成员,不管输入对象中是否有对应键。

具体来说,当metadataKeysMapper被声明为Record<string, MetadataKeys>时,typeof metadataKeysMapper[K]的类型是MetadataKeys(即MetadataKeys.NATURE | MetadataKeys.BUDGET_YEAR联合类型),映射类型会为这个联合类型的每个成员生成属性,最终返回类型始终包含nature和budget_year两个属性,自然不会对不存在的属性访问报错。

解决方案

1. 保留metadataKeysMapper的精确类型

去掉宽泛的Record<string, MetadataKeys>类型声明,改用as const让TypeScript推断出精确的键值对类型,这样TypeScript能明确知道每个输入键对应的枚举成员:

enum MetadataKeys {
  NATURE = 'nature',
  BUDGET_YEAR = 'budget_year',
}

// 用as const保留精确的键值映射类型
const metadataKeysMapper = {
  cdNatureza: MetadataKeys.NATURE,
  natureza: MetadataKeys.NATURE,
  cd_natureza: MetadataKeys.NATURE,
  SiglaNatureza: MetadataKeys.NATURE,
  nuAnoOrcamento: MetadataKeys.BUDGET_YEAR,
  ordem_orcamentaria: MetadataKeys.BUDGET_YEAR,
  nu_ano_orcamento: MetadataKeys.BUDGET_YEAR,
  Orcamento: MetadataKeys.BUDGET_YEAR,
  anoOrcamento: MetadataKeys.BUDGET_YEAR,
} as const;

2. 修正MetadataResponse<T>类型定义

重新定义返回类型,确保只包含输入对象中存在的键对应的枚举成员属性,并正确推导属性类型:

// 提取输入T中能映射到MetadataKeys的键
type MappableKeys<T> = Extract<keyof T, keyof typeof metadataKeysMapper>;

// 获取输入T对应的所有目标键(去重后的MetadataKeys成员)
type TargetKeys<T> = typeof metadataKeysMapper[MappableKeys<T>];

// 定义最终的返回类型:仅包含存在的目标键,类型为对应输入键的类型联合
type MetadataResponse<T> = {
  [K in TargetKeys<T>]: T[Extract<MappableKeys<T>, { 
    [P in keyof typeof metadataKeysMapper]: typeof metadataKeysMapper[P] extends K ? P : never 
  }[keyof typeof metadataKeysMapper]>];
};

3. 调整processMetadata函数

优化函数实现,让类型断言更安全,同时确保逻辑和类型一致:

export function processMetadata<T extends Record<string, unknown>>(
  metadata: T,
): MetadataResponse<T> {
  const processedMetadata: Partial<MetadataResponse<T>> = {};

  for (const [key, value] of Object.entries(metadata)) {
    // 缩小key的类型范围,确保它属于metadataKeysMapper的键
    if (key in metadataKeysMapper && value !== undefined) {
      const targetKey = metadataKeysMapper[key as keyof typeof metadataKeysMapper];
      processedMetadata[targetKey as keyof MetadataResponse<T>] = value as MetadataResponse<T>[typeof targetKey];
    }
  }

  return processedMetadata as MetadataResponse<T>;
}
测试验证

现在测试你的示例代码,TypeScript会正确报错:

const input = { natureza: 'comum' };
const result = processMetadata(input);

// 正常运行,类型为string
console.log(result.nature);

// 触发编译时错误:属性'budget_year'不存在于类型'{ nature: string; }'
console.log(result.budget_year);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 17:57:32