微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决使用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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。