TS - 不能将对象"as const"与值 every() 一起使用 - "此表达式不可调用"。



我有一个带有硬编码数组值的对象。这个对象是as const,因为在代码的其他部分,我需要TS知道所有可能的值。

问题是,我不能对该对象的给定数组使用.every()方法。TS遵守This expression is not callable.,即使该代码工作得很好。

我在调试时注意到三件事:

  1. 相反的方法some没有问题
  2. 如果我从对象中删除as constevery就没有错误,但正如我所说,我不能删除它,因为TS需要知道所有值,而不是将它们视为任何number
  3. 如果我在该对象上使用硬编码键而不是变量,它就可以工作(但我需要将其用作函数(

为什么这不起作用?我做错了什么?

const demo = {
a: [44, 23, 77],
b: [22, 45, 55],
} as const
const everyfunction = (key: keyof typeof demo) => demo[key]
.every((value) => value > 40) // This expression is not callable.
const somefunction = (key: keyof typeof demo) => demo[key]
.some((value) => value > 40) // works
const everyfunction2 = () => demo['a']
.every((value) => value > 40) // works

console.log(somefunction('a'))
console.log(everyfunction('a'))

实例

扩展的错误消息提供了更多线索:

Each member of the union type '...' has signatures, but none of those signatures are compatible with each other.

那么,为什么我们会得到这个错误,为什么some没有产生相同的结果呢?我们需要查看Array的声明文件,看看这些方法的函数签名实际上是什么:

every<S extends T>(
predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any
): this is S[];
every(
predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any
): boolean;
some(
predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any
): boolean;

正如您所看到的,every有一个重载版本,而some没有。every的重载版本充当类型谓词,这意味着传入的数组T被断言为S类型。这很有用,因为它可以让我们做一些事情,比如:

const arr = ["a", "a", "a"]; // string[]
if (arr.every((char): char is "a" => char === "a")) {
const typedArr = arr; // "a"[]
}

您的示例中的问题是demo[key]的类型是[44, 23, 77] | [22, 45, 55]。这种类型触发了every的重载版本,为调用提供了两个潜在的函数签名:

<S extends [44, 23, 77]>(value: [44, 23, 77], index: number, array: T[]) => value is S
<S extends [22, 45, 55]>(value: [22, 45, 55], index: number, array: T[]) => value is S

TS在编译时无法知道用这些签名调用every的结果——它们不兼容,因为它们包含不同的数字文本。另一方面,some可以被调用得很好,因为它没有这样的重载,而是创建了两种类型44 | 23 | 77 & 22 | 45 | 55的交集,从而使签名有效。

绕过这一问题的最简单方法是在进行every调用之前,将元组断言为更宽的readonly number[]

const everyfunction = (key: keyof typeof demo) => (demo[key] as readonly number[])
.every((value) => value > 40)

您也许可以将其封装在某种辅助函数中,以使用泛型来模糊断言(此处给出了最低限度(:

const everyOnObject = <O extends Record<string, readonly number[]>>(obj: O, key: keyof O) => {
return obj[key].every((value) => value > 40);
}
console.log(everyOnObject(demo, "a"));

首先将对象结构定义为接口,就可以了。

更新的示例

最新更新