为何TypeScript对对象类型推断错误却能正确推断Record类型?
TypeScript函数类型推断异常问题
我尝试编写一个TypeScript函数,它接受两种参数:要么是包含指定键且对应值为类型A的对象,要么直接是类型A的值,最终返回类型A。我将参数类型定义为Record<K, A> | A,但调用时类型推断出现错误。
代码示例
type Key = keyof any function hasKey<K extends Key>( value: unknown, key: K, ): value is { [P in K]: unknown } { return value !== null && typeof value === "object" && key in value; } function propertyOrSelf<A, K extends Key>( value: Record<K, A> | A, key: K ): A { return hasKey(value, key) ? value[key] : value; } const alice: { name: string } = { name: "Alice" } // { name: string } 和 Record<"name", string> 结构等价 const alice2: Record<"name", string> = alice const alice3: { name: string } = alice2 const bob: string = "Bob" const n1: { name: string } | string = Math.random() < 0.5 ? alice : bob const n2: Record<"name", string> | string = n1 // 报错:Type 'number' is not assignable to type 'string'.(2322) const a: string = propertyOrSelf(n1, "name"); // 正常工作 const b: string = propertyOrSelf<string, "name">(n1, "name"); const c: string = propertyOrSelf(n2, "name");
问题原因
核心在于TypeScript对具名属性类型(如{ name: string })和索引签名类型(如Record<"name", string>)的推断逻辑存在差异:
- 对于
n2(类型为Record<"name", string> | string),TypeScript能直接匹配函数参数的Record<K, A> | A结构:Record<"name", string>对应Record<K, A>(自动推断K="name"、A=string),string对应A,因此能正确得出返回类型为string。 - 对于
n1(类型为{ name: string } | string),虽然{ name: string }是Record<"name", string>的子类型,但TypeScript的类型推断引擎不会自动将具名属性类型转换为Record类型。在推断A时,它需要同时满足:{ name: string }符合Record<K, A>,且string符合A。这种约束冲突导致推断失败,最终返回类型被错误推断,引发类型不匹配报错。
解决方案
有两种可行的解决方式:
- 显式指定类型参数:调用函数时手动传入
A和K的类型,像示例中的b变量那样,强制TypeScript使用正确的类型推断路径。 - 修改函数类型定义:放弃使用
Record<K, A>,改用更通用的具名属性类型,或者用条件类型辅助推断,让函数能兼容两种类型的输入:
这个版本的函数可以正确处理// 方案:使用条件类型提取返回值类型 type PropertyOrSelf<T, K extends Key> = T extends { [P in K]: infer A } ? A : T; function propertyOrSelf<T, K extends Key>( value: T, key: K ): PropertyOrSelf<T, K> { return hasKey(value, key) ? value[key] : value as PropertyOrSelf<T, K>; }{ name: string } | string这类联合类型,无需手动指定类型参数。
内容的提问来源于stack exchange,提问作者steinybot
相关产品推荐
相关产品推荐

