如何解决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
相关产品推荐
相关产品推荐

