Open DanielRosenwasser opened 3 weeks ago
Deferred
helper type
Shouldn’t it be the other way around - a NonDeferred
helper type? If you treat “non-deferred” as the default then you end up causing the same breaks you were trying to avoid (code that assumes a callback is deferred and has its narrowings cancelled prematurely)
plus I’d think that functions calling a callback immediately are probably in the minority relative to ones where it’s deferred/called asynchronously.
plus I’d think that functions calling a callback immediately are probably in the minority relative to ones where it’s deferred/called asynchronously.
…unless someone just got finished reading a monad tutorial and went mad with power after it finally clicked for them 🚎
The idea is that this would be behind a --strict
flag which takes a more conservative view of narrowing - and declaration sites would be able to opt out of that.
Hmm… it seems like it’s probably pretty idiomatic today to do stuff like
let foo: Foo | null = createFoo();
foo.onFinished(() => {
foo = null;
});
// now foo is fully initialized, pass it around
As a matter of principle I’d feel uncomfortable passing foo
around before all its callbacks were registered, and it wouldn’t be feasible for me to turn this proposed flag on until the library writer updated their types to align, which might make it too breaky even for strict
My point stands about the potential for code bloat with Deferred
vs. NonDeferred
too
plus I’d think that functions calling a callback immediately are probably in the minority relative to ones where it’s deferred/called asynchronously.
I would postulate that most callbacks are array method callbacks, FWIW.
and it wouldn’t be feasible for me to turn this proposed flag on until the library writer updated their types to align, which might make it too breaky even for
strict
This is a bit of my concern too - that people would try this out, find it's too painful, and forget to try to turn it on again in the future.
Control Flow over Callback Arguments, and the
Deferred
Marker Typehttps://github.com/microsoft/TypeScript/pull/58729
Deferred<...>
helper type.This is more work, so how are we avoiding it?
But there is still a lot of work now resolving functons that are called to figure out if any parameter is marked with a
Deferred
.This work would have been done regardless of if a variable is actually ever referenced in the lambda.
y
even thoughy
isn't ever referenced.So instead of resolving the type of the called function immediately, we do a walk of the callback's CFG to look for assignments to
y
before we decide to resolve.But if you have an effectful function, aren't you going to have to resolve the call anyway?
But also, that's not necessarily the case. Local effects might have nothing to do with outer variables!
y
is mutated, butx
itself is never referenced.Deferred
type at that position.f(options: { f: Deferred<() => void> })
?Deferred
is a type, you can write{ [K in keyof T]: Deferred<T[K]> }
for a deferred tuple.Deferred
in certain places.Deferred<...>
is no new syntax, similar toNoInfer
.Going back to the beginning - this analysis is not just for assignments, it's also for array mutations.
Does any of this track whether or not the function is truly deferred or not?
await
ing, aliasing, etc.Though it raises some questions about
Deferred
as well.Deferred
as "next turn of the event loop" or "not immediately".