如何在 TypeScript 中正确键入 Array.map 回调?

问题描述 投票:0回答:3

我正在使用 React 和 TypeScript。当我使用 map() 迭代数组时,似乎并不总是检查类型。在下面的示例中,我将字符串“more”传递到eachItem 函数中,其中需要数字。但没有抛出错误:

function eachItem(val: number, i: number) {
  return Number(val) / 2;
}
const arr = [4, "more", 6];
arr.map(eachItem);

我想我明白为什么会发生这种情况,但我的问题是如何在地图函数上进行严格的输入,以便在传入字符串时出现此错误:

“字符串”类型的参数不可分配给“数字”类型的参数

为什么没有抛出错误是因为我给了map()回调。这个map()需要这种类型的回调:

'(value: string | number, index: number, array: (string | number)[]) => Element'

我正在给出这种类型的回调:

'(item: number, i: number) => Element'

因此,不要检查“数字”是否包含“字符串”| number',TypeScript 检查是否为 'string | number' 包含 'number' 并发现它为真。我的逻辑表明我们需要一个错误,因为我在只允许数字的地方传递了“更多”。 TypeScript 的逻辑表明没有错误,我传递了一个允许数字的函数,其中允许字符串和数字的函数是允许的。 这似乎不是一个大问题,但是当您的类型是联合时,您会在不应该的情况下收到错误。这是一个例子,结果错误: interface IMoney { cash: number; } interface ICard { cardtype: string; cardnumber: string; } type IMix = IMoney | ICard; const menu = [ {cash: 566}, {cardtype: "credit", cardnumber: "111111111111111111"} ]; menu.map((item: IMix, i: number) => (item.cash || item.cardtype));

类型“IMix”上不存在属性“cash”。
  “ICard”类型上不存在属性“cash”。

我知道,现在我必须执行以下操作,但是我无法在代码中表达现金和卡类型相互排斥:

interface IMix { cash?: number; cardtype?: string; cardnumber?: string; }

Typescript 关于第一个示例中的错误是正确的。考虑一下我们是否强制编译器接受修改后的函数:
arrays typescript callback typescript-typings
3个回答
11
投票

所以 typescript 不允许这种行为的原因是它不能被证明对于任何具有签名

(val: number, i: number) => string
 的函数都是正确的,而是取决于函数的具体实现。

现在在第二个示例中,该函数可以访问这些字段,并且不会出现运行时错误。这些字段可能未定义,但这对于此实现来说并不重要。我们可以做的是创建一个类型,其中联合中的所有可能字段都是可选的,这实际上是查看数组中项目的另一种方式(只是不是打字稿默认查看它的方式)。 Typescript 将允许我们传递带有这样的参数的函数,因为它是安全的,因为所有字段都是可选的,所以函数在使用它们之前都需要检查。


interface IMoney { cash: number; } interface ICard { cardtype: string; cardnumber: string; } type IMix = IMoney | ICard; const menu = [ { cash: 566 }, { cardtype: "credit", cardnumber: "111111111111111111" } ]; type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never type PartialUnion<T> = UnionToIntersection<T extends any ? Partial<T> : never> menu.map((item: PartialUnion<IMix>, i: number) => (item.cash || item.cardtype));

请参阅

here
了解
UnionToIntersection

的解释(并投票支持使这成为可能的答案:))

T extends any ? Partial<T> : never
将获取联合中的每个成员(因为条件类型分布在联合上)并使其成为

Partial

,这意味着所有字段现在都是可选的。然后我们使用

UnionToIntersection
将部分的并集转换为部分部分的交集。结果是一个类型,其中包含联合的每个成员中的所有字段,但所有字段都标记为部分字段。
    
您似乎想要一个

交集类型

0
投票

type IMix = IMoney & ICard;

您可以使用实用程序类型和类型操作功能来处理具有可选属性的类型组合

0
投票

可选映射 T 的每个属性并使它们成为可选

与接受的答案相比,这可能是实现相同结果的更简单方法

© www.soinside.com 2019 - 2024. All rights reserved.