Uh oh!
There was an error while loading. Please reload this page.
Analyze control flow effects of lambdas passed as arguments - #58729
Analyze control flow effects of lambdas passed as arguments#58729Anders Hejlsberg (ahejlsberg) wants to merge 33 commits into
Conversation
Anders Hejlsberg (ahejlsberg)
commented
May 31, 2024
TypeScript Bot (@typescript-bot) test it |
TypeScript Bot (typescript-bot)
commented
May 31, 2024
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. Everything looks the same! |
TypeScript Bot (typescript-bot)
commented
May 31, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing Something interesting changed - please have a look. Details
|
TypeScript Bot (typescript-bot)
commented
May 31, 2024
Anders Hejlsberg (@ahejlsberg) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Bruce Pascoe (fatcerberus)
commented
May 31, 2024
I feel like this is just going to flip the problem on its head in the end - instead of people complaining because their immediately-invoked callbacks didn't affect narrowing, an equal number of people will probably now complain instead that callbacks they know will be called out-of-band do affect narrowing. The goalposts don't even move really - the other team just gets possession is all 😄 |
TypeScript Bot (typescript-bot)
commented
Jun 1, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
Anders Hejlsberg (ahejlsberg)
commented
Jun 1, 2024
I think it's clear that there's no one-size-fits-all answer to whether effects of executing lambda arguments should be reflected in CFA types. Our current assumption is that lambdas are never executed synchronously. This PR experiments with the assumption they're possibly executed, which, given no additional information about the function to which the lambda arguments are passed, is definitely a more sound assumption. Also, the PR validates an implementation strategy and gives us data on the performance cost of analyzing lambda effects. I would nice to avoid modifiers (like |
Anders Hejlsberg (ahejlsberg)
commented
Jun 3, 2024
TypeScript Bot (@typescript-bot) test it |
TypeScript Bot (typescript-bot)
commented
Jun 3, 2024
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. Everything looks the same! |
TypeScript Bot (typescript-bot)
commented
Jun 3, 2024
Anders Hejlsberg (@ahejlsberg) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TypeScript Bot (typescript-bot)
commented
Jun 3, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing Something interesting changed - please have a look. Details
|
TypeScript Bot (typescript-bot)
commented
Jun 4, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
Anders Hejlsberg (@ahejlsberg) If I'm understanding correctly, this would apply to arguments that are declared as arrow functions or function expressions, but not function declarations. Is that right? If so, why exclude function declarations? (FWIW, I do like this experiment and the attempt at added strictness, if it ends up not being too breaking.) |
Anders Hejlsberg (ahejlsberg)
commented
Jun 5, 2024
Ethan Resnick (@ethanresnick) It only applies to arrow functions and function expressions passed directly as arguments. It does not apply to functions referenced through identifiers or other indirect expression constructs. |
TypeScript Bot (typescript-bot)
commented
Jun 5, 2024
Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page. Also, please make sure Daniel Rosenwasser (@DanielRosenwasser) and Ryan Cavanaugh (@RyanCavanaugh) are aware of the changes, just as a heads up. |
Anders Hejlsberg (ahejlsberg)
commented
Jun 5, 2024
TypeScript Bot (@typescript-bot) test it |
TypeScript Bot (typescript-bot)
commented
Aug 8, 2024
Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready. Everything looks the same! |
TypeScript Bot (typescript-bot)
commented
Aug 8, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the user tests with tsc comparing Something interesting changed - please have a look. Details
|
TypeScript Bot (typescript-bot)
commented
Aug 8, 2024
Anders Hejlsberg (@ahejlsberg) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
TypeScript Bot (typescript-bot)
commented
Aug 8, 2024
Anders Hejlsberg (@ahejlsberg) Here are the results of running the top 400 repos with tsc comparing Something interesting changed - please have a look. Details
|
Anders Hejlsberg (ahejlsberg)
commented
Aug 8, 2024
TypeScript Bot (@typescript-bot) perf test this faster |
TypeScript Bot (typescript-bot)
commented
Aug 8, 2024
Anders Hejlsberg (@ahejlsberg) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Jake Bailey (jakebailey)
commented
Aug 8, 2024
TypeScript Bot (@typescript-bot) perf test this faster predictable=true |
TypeScript Bot (typescript-bot)
commented
Aug 9, 2024
Jake Bailey (@jakebailey) Here they are:tscComparison Report - baseline..pr
System info unknown Hosts
Scenarios
Developer Information: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Anders Hejlsberg (ahejlsberg)
commented
Aug 9, 2024
Jake Bailey (@jakebailey) Asking for predictable seems to have very unpredictable effects. 😮 |
Anders Hejlsberg (ahejlsberg)
commented
Aug 9, 2024
Jake Bailey (@jakebailey) The issue with switching to actual |
Jake Bailey (jakebailey)
commented
Aug 9, 2024
The error bars are 3-4x smaller! 😄 But yes, don't use this feature, I'm still not sure if it's a good thing or not.
Ah, right. Theoretically we can exclude |
Jake Bailey (jakebailey)
commented
Aug 9, 2024
Even with the keyword, #59580 (comment) shows roughly the same perf regression in bind. Still not sure what it could be reading |
I'm just a passer-by (who writes a lot of TypeScript), who got excited by the prospect of a new keyword that would improve CFA or soundness, but I'm not sure I understand the motivation here, relative to the magnitude of the change (new syntax). I guess it started as an effort to improve soundness by considering that called functions might have side effects, and now it basically helps correct for overzealous CFA. Here is another case (not related to this feature) where CFA thinks it knows everything and narrows the type annotation of a local variable: functionfoo(a: number[],i: number){// I'm serious, Typescript, I'm saying this could be out-of-bounds!constn: number|undefined=a[i];constn2=n;// : number}In both cases, you have to occasionally override CFA, e.g. in the example at the top of this thread, one could write: declarefunctionmystery(cb: ()=>void): void;letx: string|number="OK"asstring|number;mystery(()=>{x=10;});if(x===10){// works}In terms of the larger issue of side effects, I think it should at least be acknowledged that passing any closure to any function could lead to functions having side effects that affect local variables; it doesn't really depend on whether a callback is invoked "immediately" or "deferred." For example: constfoo=newFoo(()=>{/* ... set a local variable */});foo.execute();Here, the callback is not called immediately, but it is called during the course of the function. The point is, the question of whether a callback might be called immediately or not by a function is only loosely connected to the question of whether a function call has side effects that might affect the analysis of local variables. I think an annotation that says "this callback is called exactly once, synchronously" or "this callback is called at least once, synchronously" might even be more useful? It would serve a slightly different purpose. If this keyword opens the door to other keywords, I'm for it. There are a lot of keywords I would want. |
Simon Schick (SimonSchick)
commented
Nov 14, 2024
Is this going to make the 5.7 release? I see it is in the iteration plan and I'm curious what work is remaining here 👀 |
Jake Bailey (jakebailey)
commented
Nov 14, 2024
No, 5.7 is cut, and its release candidate is already out. https://devblogs.microsoft.com/typescript/announcing-typescript-5-7-rc/ Something being in the iteration plan does not guarantee its completion / inclusion in a release. |
TypeScript Bot (typescript-bot)
commented
Mar 24, 2026
With 6.0 out as the final release vehicle for this codebase, we're closing all PRs that don't fit the merge criteria for post-6.0 patches. If you think this was a mistake and this PR fits the post-6.0 patch criteria, please post to the 6.0 iteration issue with details (specifically, which PR and which patch criteria it satisfies). Next steps for PRs:
|
This PR introduces a new
immediatemodifier that can be applied to callback parameters to indicate that control flow analysis should assume that function expressions or arrow functions (in the following called lambda expressions) passed as arguments in a function call may be invoked synchronously during that call. For example:Without the
immediatemodifier, control flow analysis will assume that the lambda expression is never invoked synchronously and thus will consider the type ofxto bestringfollowing the call. With theimmediatemodifier, control flow analysis assumes that the lambda expression may have been invoked synchronously and thus considers the type ofxto bestring | numberfollowing the call.It an error to apply the
immediatemodifier to anything but a parameter with a type that permits functions. Async arrow functions, async function expressions, and generator function expressions are always assumed to be deferred, regardless of whether their corresponding parameter includes animmediatemodifier.In JavaScript files, an immediate callback parameter can be declared using an
/** @immediate */JSDoc annotation:Since the
immediatemodifier is considered an error by earlier versions of TypeScript, it isn't possible to addimmediatemodifiers to frameworks and libraries without also requiring the latest version of the compiler. The compiler therefore also recognizes/** @immediate */JSDoc comments in TypeScript files such that they can be used in cases where backwards compatibility matters. However, use ofimmediatemodifiers is recommended whenever possible.NOTE: This PR initially changed control flow analysis to assume possibly-immediately-called semantics by default and implemented a
deferredmodifier to indicate never-immediately-called semantics. While technically more sound (absent knowledge of whether or when a callback is called, it is safest and most conservative to assume it may have been called), this turned out to require too much remediation in existing code. The PR now preserves the default never-immediately-called semantics and allows code to opt into possibly-immediately-called analysis using theimmediatemodifier.Fixes#11498.
Fixes#15380.
Fixes#57880.