我看到一些我不理解的非常奇怪的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)
游乐场链接