使用Object.values和Records的联合时避免隐式的“ any”

问题描述

为什么下面的代码无提示地假设fooValuesany。并且有可能避免这种隐式any。我尝试了“ noImplicitAny” tsconfig选项,但这似乎无济于事。

type Record1 = Record<string,{ a: 1 }>
type Record2 = Record<string,{ b: 1 }>
const foo: Record1 | Record2 = {}
const bar: Record1 = {}
// Why is `fooValues` any[]???
const fooValues = Object.values(foo)
// This one is OK.
const barValues = Object.values(bar)

Playground link

解决方法

这与隐式any无关。您在这里看到的any实际上非常明确,并且与standard library's call signatures for Object.entries()有关:

interface ObjectConstructor {
  values<T>(o: { [s: string]: T } | ArrayLike<T>): T[];
  values(o: {}): any[];
}

这是一个带有两个呼叫签名的overloaded函数。第一个是泛型​​函数,需要带有索引签名的参数并返回一个强类型数组。第二个是非泛型函数,它接受任何非空参数并返回any[]。当您调用Object.values()时,编译器必须通过依次尝试每个签名来选择要应用的调用签名。显然,当您致电Object.values(bar)时,它将选择第一个签名,而当您致电Object.values(foo)时,它将选择第二个签名。


让我们通过将每个调用签名分解为自己的独立函数来调查正在发生的事情:

const objectValues1: <T>(o: { [s: string]: T; } | ArrayLike<T>) => T[] = Object.values;
const objectValues2: (o: {}) => any[] = Object.values;

在这里,objectValues1仅具有通用签名,objectValues2仅具有非通用签名。让我们看一下bar

const barVals1 = objectValues1(bar); // okay {a: 1}[]
// T is inferred as {a: 1}

太好了,objectValues1()可以工作; T被推断为{a: 1},您将得到一个强类型的输出。现在让我们尝试foo

const fooVals1 = objectValues1(foo); // error!
// T inferred as {a: 1},but
/* Argument of type 'Record<string,{ a: 1; }> | Record<string,{ b: 1; }>' 
is not assignable to parameter of type 'ArrayLike<{ a: 1; }> | { [s: string]: { a: 1; }; }'. */

const fooVals2 = objectValues2(foo); // any[]

糟糕,该方法不起作用,我们不得不尝试产生objectValues2()的{​​{1}}。但是为什么不起作用?


如果您看到的话,编译器只是将any推断为T而不是并集{a: 1}。泛型参数推断是一门艺术,而不是一门科学。好吧,对人类而言。对于编译器,类型参数推断是由语言设计者想出的一些试探法来进行的。这些启发式方法通常会拒绝要求综合任何推理站点中不存在的并集类型的呼叫。为什么?因为事实证明这最终会接受不想要的东西。有关更多信息,请参见this question{a: 1} | {b: 1}的类型将foo{a: 1}都表示为{b: 1}的可能候选者,但是由于这两种选择都不起作用,因此推论失败,并且编译器进行到第二个调用签名


那么,你能做什么?好吧,最简单的方法就是手动将泛型类型参数指定为并集。编译器不会为您推断出它,但是如果您指定它,它将接受它:

T

Playground link to code

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...