TypeScript在回调函数(例如.filter和.some回调函数)内部丢失了类型知识



我看到一些我不理解的非常奇怪的TypeScript行为。这是一个场景:

我有三个不同的接口来描述一个事件。它们看起来像这个

interface ReceiveMessageEvent {
event: 'receive-message';
payload: {
action: 'receive-message';
businessSMSId: number;
message: {
businessSMSId: number;
from: string;
to: string;
};
};
}
interface ReadMessageEvent {
event: 'read-message';
payload: {
action: 'read-message';
businessSMSId: number;
customerNumber: string;
inboxNumber: string;
};
}
interface SetThreadsEvent {
event: 'set-threads';
threads: any[];
}

正如您所看到的,所有三个接口都有一个event属性,该属性是一个特定的字符串,这意味着只能有三个可能的选项,"接收消息"、"读取消息"或"设置线程"。这很重要,因为接下来我有一个switch语句,其中的情况是这个事件属性:

switch (action.payload.event) {
case 'receive-message':
// ...
case 'read-message':
// ...
case 'set-threads':
// ...
}

因此,由于case是事件属性,我希望TypeScript能够根据我们所处的情况知道其余数据的形状。现在,is确实正确地做到了这一点(正如我所期望的(,但在一个特定的场景中除外,这就是我的困惑所在。这种情况是当我在.filter方法的回调函数内部时。

例如:

switch (action.payload.event) {
case 'receive-message':
// This is correct and has no errors
const test = action.payload.payload.message.to
// This is giving me a TS error, even though I'm accessing the exact same property
// As in the line above
myArray.filter((thread) => thread.property === action.payload.payload.message.to)
^^^^^^^^ TS error here
case 'read-message':
// ...
case 'set-threads':
// ...
}

上面例子的确切错误是:

Property 'payload' does not exist on type 'ReceiveMessageEvent | ReadMessageEvent | SetThreadsEvent'.
Property 'payload' does not exist on type 'SetThreadsEvent'.

一旦我进入回调函数,Typescript就好像失去了它的类型知识。代码确实和预期的一样工作,只是TypeScript告诉我有一个错误,尽管看起来没有。

最后,我要注意的是,我可以这样做一些选角,然后错误就会消失,尽管我宁愿不这样做,除非我绝对必须这样做(:

switch (action.payload.event) {
case 'receive-message':
myArray.filter((thread) => thread.property === (action.payload as ReceiveMessageEvent).payload.message.to)
case 'read-message':
// ...
case 'set-threads':
// ...
}

这种行为有原因吗,或者这可能是TypeScript中的错误吗?

一旦我进入回调函数,Typescript就好像失去了它的类型知识

是的,就是这样。这种行为是故意的。或者至少,这是最好的打字脚本可以做

问题是typescript不知道何时调用回调函数。您和我都知道,对于.filter,回调将被同步调用,其间没有任何内容,但并非所有回调都是这样。例如,setTimeout中的回调将被异步调用。

类型信息不能确定它是否是同步的,所以typescript必须假设最坏的情况:异步。如果异步调用回调,那么任何任意代码都可能在回调之前运行,因此您为缩小类型所做的检查可能不再准确。在此期间,某些代码可能更改了属性。

对此,最简单的修复方法通常是在非回调代码中将您关心的值分配给常量,然后在回调代码中引用该常量。使用const,typescript可以假设它没有被重新分配。

case 'receive-message':
const test = action.payload.payload.message.to
myArray.filter((thread) => thread.property === test)
// OR:
case 'receive-message':
const payload = action.payload.payload
myArray.filter((thread) => thread.property === payload.message.to)

游乐场链接

相关内容

  • 没有找到相关文章

最新更新