控制流程回调参数以及 Deferred
标记类型
#58729
- 想要能够追踪回调作为控制流的可能分支。
- 不幸的是,有很多回调是排队并以延迟方式运行的。
- 需要一种方式来表示任何可能的函数调用都不会立即发生。
- 我们采取的方法是将回调的类型 Package 在一个
Deferred<...>
帮助类型中。 - 这需要更多的工作,那么我们是如何避免它的呢?
- 我们引入了在 TS 5.5 中的一些关于折叠控制流图中无效果的部分的优化 - 这有所帮助。
- 但现在还有大量的工作要做,解决被调用的函数以确定是否有任何参数被标记为
Deferred
。 - 这种工作本来无论变量是否实际上在 lambda 中被引用都会完成。
doStuff(() => {
x = 123;
});
y;
- 在上面的示例中,我们的回调已知是有效果的 - 因此我们需要对其进行遍历以确定
y
的类型,即使 y
从不被引用。 - 因此,而不是立即解析被调用函数的类型,我们在决定解析之前对回调的 CFG 进行遍历以查找对
y
的赋值。 - 但是,如果你有一个有效函数,你不是必须解析调用吗?
- 一个较少不必要的前期工作会触发更少的循环。
- 但这并不一定是这样。 本地效果可能与外部变量无关!
let x = 0;
doStuff(() => {
let y = 123;
y = 456;
return y;
});
x;
- 在上面的示例中,
y
被改变,但 x
本身从未被引用。 - 当有多个重载时会发生什么 - 一个是延迟的?
- 我们不会进行完整的重载解析 - 我们只是查找是否有任何签名在该位置具有
Deferred
类型。 - 这很奇怪,因为这是一个内置类型,而不是与参数/绑定相关的东西。
- 你可以在其他地方使用它吗?
- 等等,你不能写类似
f(options: { f: Deferred<() => void> })
的东西吗? - 你可以写,但它不起作用。
- 如果
Deferred
是一个类型,你可以为延迟元组写 { [K in keyof T]: Deferred<T[K]> }
。 - 然后将其传播开来?
- ...这取决于你想何时解析。目前我们不做。
- 这说明这更多是一种参数修饰符 - 因为你不希望给人留下这样的印象,即它可以用于 任何地方。
- 我们可以在某些地方禁止使用
Deferred
。 - 目前只允许在参数中使用它,并在未来扩展。
- 很高兴
Deferred<...>
没有新的语法,类似于 NoInfer
。 - 回到开头 - 这个分析不仅仅是为了赋值,也是为了数组变异。
let a = [];
a.push("abc");
invoke(() => {
a.push(123);
});
a; // Array<number | string>
- 这是否跟踪了函数是否真正是延迟还是不是的问题?
- 不,这不是一个简单的问题来真正回答。
- 关于
await
、别名等的复杂性。 - 尽管这也引发了一些关于
Deferred
的问题。
let fns: Array<() => void> = [];
function enqueue(f: Deferred<() => void>) {
// Is it okay to push a Deferred function in here?
fns.push(f);
}
function runThem() {
for (const f of fns) {
f();
}
}
let y: number | string = 0;
enqueue(() => {
y = "abc";
})
y; // We correctly see that the narrowed type of 'y' is 'number' because we used 'Deferred'.
let x: number | string = 0;
enqueue(() => {
x = "abc";
})
runThem();
x; // Because we wrote 'Deferred', we will *incorrectly* say that the narrowed type is 'number'.
- 这在多大程度上取决于你如何严格地将
Deferred
视为“下一个事件循环”或“不是立即”的问题?
6条答案
按热度按时间inn6fuwd1#
Deferred
辅助类型难道不应该反过来吗?一个
NonDeferred
辅助类型?如果你把“非延迟”当作默认值,那么你最终会导致你试图避免的同样的问题(假设回调是延迟的,并且过早地取消了它的缩小)此外,我认为立即调用回调的函数相对于延迟/异步调用的函数可能是少数。
nuypyhwy2#
我认为,立即调用回调函数的功能相对于延迟/异步调用的功能来说可能是少数。
...除非有人刚刚读完一个monad教程,在它终于对他们有意义时,他们感到非常兴奋🚎
4sup72z83#
这个想法是将这个放在一个
--strict
标志后面,该标志对缩小持更保守的观点-并且声明站点可以选择退出。yqyhoc1h4#
嗯...似乎在今天,做类似的事情可能很符合习惯。
作为原则,我觉得在所有回调函数都被注册之前传递
foo
会让我感到不舒服,而且在我把这个建议的标志打开之前,库作者更新他们的类型以对齐是不可能的,这可能会让strict
变得过于不稳定。我的观点也适用于
Deferred
与NonDeferred
之间潜在的代码膨胀问题。idfiyjo85#
我认为,相对于延迟调用或异步调用的回调函数,立即调用回调函数的函数可能属于少数。
我假设大多数回调函数是数组方法回调函数,仅供参考。
sg3maiej6#
这对我来说是不可行的,直到图书馆编写者更新他们的类型以对齐,这可能会使它对于
strict
来说过于脆弱。这也是我的担忧之一——人们会尝试这个功能,发现它太痛苦了,然后忘记在未来再次尝试打开它。