【发布时间】:2022-02-07 10:29:13
【问题描述】:
我有一个变量,它的类型是不同数组类型的联合。我想将其类型缩小到该联合的单个成员,因此我可以为每种类型在其上运行适当的代码。在这里使用 Array.every 和 custom type guard 似乎是正确的方法,但是当我尝试这个 TypeScript 时抱怨“这个表达式不可调用”。以及我不明白的解释。
这是我的最小可重现示例:
const isNumber = (val: unknown): val is number => typeof val === 'number';
const unionArr: string[] | number[] = Math.random() > 0.5 ? [1, 2, 3, 4, 5] : ['1', '2', '3', '4', '5'];
if (unionArr.every(isNumber)) { // <- Error
unionArr;
}
这是错误:
This expression is not callable.
Each member of the union type
'{
<S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): this is S[];
(predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): boolean;
} | {
...;
}'
has signatures, but none of those signatures are compatible with each other.
这并不妨碍我继续。我发现在缩小其类型之前使用type assertion 将我的数组重铸为unknown[],就像我编写isNumberArray 自定义类型保护时所做的那样,可以在不影响类型安全的情况下消除错误。
我还发现将我的 string[] | number[] 数组重铸为 (string | number)[] 可以消除错误。
但是,数组的类型似乎没有正确缩小,所以我需要在检查后使用额外的as number[]:
const isNumber = (val: unknown): val is number => typeof val === 'number';
const unionArr: string[] | number[] = Math.random() > 0.5 ? [1, 2, 3, 4, 5] : ['1', '2', '3', '4', '5'];
if ((unionArr as unknown[]).every(isNumber)) { // <- No error
unionArr; // <- Incorrectly typed as string[] | number[]
}
if ((unionArr as (string | number)[]).every(isNumber)) { // <- No error
unionArr; // <- Incrrectly typed as string[] | number[]
}
我也尝试与非数组联合进行比较,当然在这种情况下我只是直接使用自定义类型保护而不是与Array.every 一起使用。在那种情况下,也没有错误并且类型被正确缩小:
const isNumber = (val: unknown): val is number => typeof val === 'number';
const union: string | number = Math.random() > 0.5 ? 1 : '1';
if (isNumber(union)) {
union; // <- Correctly typed as number
}
因为我有安全的类型断言解决方法,我可以继续而不需要理解这一点。但是我仍然对为什么首先出现该错误感到非常困惑,因为我正在尝试将类型联合缩小到该联合的单个成员。
我猜这与 TypeScript 输入 Array.every 的方式有关,除了我已经在使用的解决方法之外,我可能无能为力。但是当我真的不明白出了什么问题时,很难确定这一点。有什么我可以在这里做不同的事情吗,或者as unknown[] 类型断言我使用了正确或最好的方法来处理这个问题?
【问题讨论】:
标签: arrays typescript narrowing