Skip to content

Preserve type refinements in closures created past last assignment - #56908

Merged
Anders Hejlsberg (ahejlsberg) merged 14 commits into
mainfrom
fix35124
Jan 9, 2024
Merged

Preserve type refinements in closures created past last assignment#56908
Anders Hejlsberg (ahejlsberg) merged 14 commits into
mainfrom
fix35124

Conversation

@ahejlsberg

@ahejlsbergAnders Hejlsberg (ahejlsberg) commented Dec 31, 2023

Copy link
Copy Markdown
Member

We currently preserve type refinements in closures for const variables and parameters that are never targeted in assignments. With this PR we also preserve type refinements for parameters, local let variables, and catch clause variables in closures that are created past the last assignment to those parameters or variables. For example:

declarefunctionaction(cb: ()=>void): void;functionf1(){letx: string|number;x="abc";action(()=>{x});// x has type string | numberx=42;action(()=>{x});// x has type number}

Above, it is unknown when (or even if) the action function will invoke a callback function previously passed to it. Thus, when the first arrow function is created, it is unknown whether it will be invoked with x having type string or number because there are subsequent assignments to x. However, the second arrow function is created past the last assignment to x, so x can safely be narrowed to type number in that arrow function.

A similar example that adjusts an argument value before using it in a closure:

functionmakeAdder(n?: number){n??=0;return(m: number)=>n+m;// Now ok, previously was error}

Type refinements are not preserved in inner function and class declarations (due to hoisting):

functionf2(){letx: string|number;x=42;leta=()=>{x/* number */};letf=function(){x/* number */};letC=class{foo(){x/* number */}};leto={foo(){x/* number */}};functiong(){x/* string | number */}classA{foo(){x/* string | number */}}}

Implicit any variables have a known type following the last assignment:

functionf3(){letx;x="abc";action(()=>{x});// Implicit any errorx=42;action(()=>{x/* number */});}

Type refinements for catch variables are preserved past the last assignment (if any):

functionf4(){try{}catch(e){if(einstanceofError){letf=()=>{e/* Error */}}}}

Note that effects of assignments in compound statements extend to the entire statement:

functionf5(cond: boolean){letx: number|undefined;if(cond){x=1;action(()=>{x/* number | undefined */});}else{x=2;action(()=>{x/* number | undefined */});}action(()=>{x/* number */});}

Above, the type of x is narrowed to number only in the last arrow function, even though it would be safe to narrow in the other two arrow functions. That, however, would require full control flow analysis which is significantly more complex and expensive than the single pass we do now.

This PR fixes multiple issues that have previously been attributed to #9998.

Fixes#13142.
Fixes#13560.
Fixes#13572.
Fixes#14748.
Fixes#16285.
Fixes#17240.
Fixes#17449.
Fixes#19606.
Fixes#19683.
Fixes#19698.
Fixes#19918.
Fixes#22120.
Fixes#22635.
Fixes#23776.
Fixes#29392.
Fixes#29916.
Fixes#31266.
Fixes#32625.
Fixes#33319.
Fixes#34669.
Fixes#35124.
Fixes#37339.
Fixes#38755.
Fixes#40202.
Fixes#43827.
Fixes#44218.
Fixes#46118.
Fixes#50580.
Fixes#52104.
Fixes#55528.
Fixes#56854.
Fixes#56973.

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the diff-based user code test suite on this PR at 2739399. You can monitor the build here.

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at 2739399. You can monitor the build here.

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the tsc-only perf test suite on this PR at 2739399. You can monitor the build here.

Update: The results are in!

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the diff-based top-repos suite on this PR at 2739399. You can monitor the build here.

Update: The results are in!

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Angular - node (v18.15.0, x64)
Memory used295,445k (± 0.02%)295,482k (± 0.01%)~295,441k295,536kp=0.128 n=6
Parse Time2.65s (± 0.19%)2.65s (± 0.21%)~2.64s2.65sp=0.640 n=6
Bind Time0.83s (± 0.66%)0.82s (± 0.00%)~0.82s0.82sp=0.071 n=6
Check Time8.15s (± 0.20%)8.17s (± 0.30%)~8.15s8.21sp=0.250 n=6
Emit Time7.10s (± 0.21%)7.10s (± 0.27%)~7.08s7.12sp=1.000 n=6
Total Time18.72s (± 0.14%)18.74s (± 0.18%)~18.70s18.80sp=0.413 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used192,499k (± 1.23%)192,488k (± 1.26%)~191,473k197,449kp=0.230 n=6
Parse Time1.34s (± 0.91%)1.35s (± 0.38%)~1.35s1.36sp=0.114 n=6
Bind Time0.72s (± 0.00%)0.72s (± 0.00%)~0.72s0.72sp=1.000 n=6
Check Time9.26s (± 0.52%)9.30s (± 0.50%)~9.23s9.35sp=0.147 n=6
Emit Time2.61s (± 0.67%)2.62s (± 0.76%)~2.59s2.64sp=0.669 n=6
Total Time13.94s (± 0.39%)13.99s (± 0.37%)~13.91s14.04sp=0.145 n=6
Monaco - node (v18.15.0, x64)
Memory used347,397k (± 0.00%)347,398k (± 0.01%)~347,364k347,415kp=0.688 n=6
Parse Time2.46s (± 0.31%)2.46s (± 0.49%)~2.44s2.47sp=0.867 n=6
Bind Time0.92s (± 0.56%)0.93s (± 0.81%)~0.92s0.94sp=0.247 n=6
Check Time6.85s (± 0.29%)6.88s (± 0.34%)~6.85s6.92sp=0.061 n=6
Emit Time4.06s (± 0.57%)4.05s (± 0.24%)~4.04s4.06sp=0.316 n=6
Total Time14.30s (± 0.23%)14.32s (± 0.22%)~14.29s14.38sp=0.466 n=6
TFS - node (v18.15.0, x64)
Memory used302,730k (± 0.00%)302,752k (± 0.01%)+22k (+ 0.01%)302,730k302,778kp=0.020 n=6
Parse Time2.00s (± 1.43%)1.98s (± 1.30%)~1.95s2.02sp=0.373 n=6
Bind Time1.00s (± 1.17%)1.00s (± 1.26%)~0.99s1.02sp=0.801 n=6
Check Time6.30s (± 0.31%)6.30s (± 0.13%)~6.29s6.31sp=0.806 n=6
Emit Time3.58s (± 0.42%)3.59s (± 0.34%)~3.57s3.60sp=0.805 n=6
Total Time12.88s (± 0.28%)12.87s (± 0.16%)~12.84s12.90sp=0.517 n=6
material-ui - node (v18.15.0, x64)
Memory used506,824k (± 0.00%)506,839k (± 0.00%)~506,821k506,856kp=0.298 n=6
Parse Time2.57s (± 0.16%)2.59s (± 0.55%)+0.02s (+ 0.71%)2.57s2.61sp=0.021 n=6
Bind Time0.99s (± 0.90%)0.99s (± 0.55%)~0.99s1.00sp=0.341 n=6
Check Time16.87s (± 0.17%)16.99s (± 0.35%)+0.12s (+ 0.72%)16.92s17.06sp=0.005 n=6
Emit Time0.00s (± 0.00%)0.00s (± 0.00%)~0.00s0.00sp=1.000 n=6
Total Time20.43s (± 0.15%)20.58s (± 0.29%)+0.14s (+ 0.70%)20.51s20.66sp=0.005 n=6
xstate - node (v18.15.0, x64)
Memory used512,828k (± 0.01%)512,920k (± 0.01%)+92k (+ 0.02%)512,836k512,988kp=0.031 n=6
Parse Time3.27s (± 0.23%)3.27s (± 0.32%)~3.26s3.29sp=0.931 n=6
Bind Time1.54s (± 0.53%)1.54s (± 0.41%)~1.53s1.55sp=0.432 n=6
Check Time2.82s (± 0.82%)2.84s (± 0.62%)~2.82s2.87sp=0.324 n=6
Emit Time0.07s (± 0.00%)0.07s (± 5.69%)~0.07s0.08sp=0.405 n=6
Total Time7.69s (± 0.22%)7.73s (± 0.25%)+0.04s (+ 0.48%)7.70s7.75sp=0.013 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg) Here are the results of running the user test suite comparing main and refs/pull/56908/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Package install failed"

Otherwise...

Something interesting changed - please have a look.

Details

adonis-framework

/mnt/ts_downloads/adonis-framework/tsconfig.json

  • [MISSING] error TS18048: 'avoid' is possibly 'undefined'.
    • /mnt/ts_downloads/adonis-framework/node_modules/adonis-framework/src/View/Form/index.js(75,11)

async

/mnt/ts_downloads/async/tsconfig.json

  • [MISSING] error TS2722: Cannot invoke an object which is possibly 'undefined'.
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(2524,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(2666,16)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(3022,25)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(3567,16)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(3691,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(5107,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(5163,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(5225,25)
  • [MISSING] error TS2684: The 'this' context of type 'Function | undefined' is not assignable to method's 'this' of type 'Function'.
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(3025,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(5228,9)
  • [MISSING] error TS18048: 'callback' is possibly 'undefined'.
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(3025,9)
    • /mnt/ts_downloads/async/node_modules/async/dist/async.js(5228,9)

lodash

/mnt/ts_downloads/lodash/tsconfig.json

  • [NEW] error TS2345: Argument of type '{ cap?: boolean | undefined; curry?: boolean | undefined; fixed?: boolean | undefined; immutable?: boolean | undefined; rearg?: boolean | undefined; }' is not assignable to parameter of type 'Function'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/fp/_baseConvert.js(262,57)
  • [MISSING] error TS18048: 'start' is possibly 'undefined'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/_overRest.js(20,42)
    • /mnt/ts_downloads/lodash/node_modules/lodash/_overRest.js(24,27)
    • /mnt/ts_downloads/lodash/node_modules/lodash/_overRest.js(27,27)
    • /mnt/ts_downloads/lodash/node_modules/lodash/_overRest.js(28,22)
    • /mnt/ts_downloads/lodash/node_modules/lodash/core.js(1450,44)
    • /mnt/ts_downloads/lodash/node_modules/lodash/core.js(1454,29)
    • /mnt/ts_downloads/lodash/node_modules/lodash/core.js(1457,29)
    • /mnt/ts_downloads/lodash/node_modules/lodash/core.js(1458,24)
  • [MISSING] error TS2538: Type 'undefined' cannot be used as an index type.
    • /mnt/ts_downloads/lodash/node_modules/lodash/_overRest.js(31,15)
    • /mnt/ts_downloads/lodash/node_modules/lodash/core.js(1461,17)
    • /mnt/ts_downloads/lodash/node_modules/lodash/spread.js(53,22)
  • [MISSING] error TS2345: Argument of type '{ cap?: boolean | undefined; curry?: boolean | undefined; fixed?: boolean | undefined; immutable?: boolean | undefined; rearg?: boolean | undefined; } | undefined' is not assignable to parameter of type 'Function'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/fp/_baseConvert.js(262,57)
  • [MISSING] error TS2722: Cannot invoke an object which is possibly 'undefined'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/mapKeys.js(31,29)
    • /mnt/ts_downloads/lodash/node_modules/lodash/mapValues.js(38,34)
    • /mnt/ts_downloads/lodash/node_modules/lodash/pickBy.js(33,12)
    • /mnt/ts_downloads/lodash/node_modules/lodash/transform.js(60,12)
  • [MISSING] error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/nthArg.js(28,26)
  • [MISSING] error TS18048: 'string' is possibly 'undefined'.
    • /mnt/ts_downloads/lodash/node_modules/lodash/template.js(199,15)

puppeteer

packages/browsers/test/src/tsconfig.json

webpack

tsconfig.json

  • [NEW] error TS2367: This comparison appears to be unintentional because the types 'boolean' and 'RuntimeSpec' have no overlap.

tsconfig.types.json

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Anders Hejlsberg (@ahejlsberg), the results of running the DT tests are ready.
Everything looks the same!
You can check the log here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg) Here are the results of running the top-repos suite comparing main and refs/pull/56908/merge:

Something interesting changed - please have a look.

Details

quilljs/quill

1 of 2 projects failed to build with the old tsc and were ignored

packages/quill/tsconfig.json

!!! error TS2339: Property 'foo' does not exist on type '{ kind: "bar"; bar: number; }'.
}
else {
obj.bar; // Not narrowed because obj is mutable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess these comments are outdated now

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll fix those.

@Andarist

Mateusz Burzyński (Andarist) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Shouldn't the assigned type be preserved at least in the second branch?

declarefunctionaction(cb: ()=>void): voidfunctiontest(arg: string|number|undefined){if(arg===undefined){arg='foo'action(()=>{arg// ^?})}else{arg=42action(()=>{arg// ^?})}}

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

Shouldn't the assigned type be preserved at least in the second branch?

Ideally, the assigned type would be preserved in both branches since the closures are both created following the last assignment. That, however, would require full control flow analysis which is significantly more complex and expensive than the single pass we do now. The next best alternative, at least in my opinion, is to extend assignment effects in compound statements to the entire statement. That's what the PR currently does. It is a bit more conservative than just using the lexical position of the last assignment, but it is more consistent.

@Andarist

Copy link
Copy Markdown
Contributor

Ideally, the assigned type would be preserved in both branches since the closures are both created following the last assignment.

Yeah, that would - indeed - be the dream :P

I haven't spent much time so far understanding how CFA is implemented. Is it always an append-only graph that is attached to nodes? In other words, is the complexity here that we can't easily peek into flow nodes that follow a specific node aka it's not easy to assess if the control flow is joined with another branch later on?

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Dec 31, 2023

Copy link
Copy Markdown
MemberAuthor

In other words, is the complexity here that we can't easily peek into flow nodes that follow a specific node

Right, the control flow graph is a "reverse linked" graph optimized for exploring execution effects that occurred prior to reaching a specific node. It is less well suited for exploring execution effects that occur after reaching a node.

The most similar scenario we currently handle in the type checker is definite property initialization in constructors (the isPropertyInitializedInConstructor function). For that, we construct a "fake" return control flow node that reflects all possible points of exit from the constructor. By pretending to access a property at that imaginary location we can ensure there are no surviving and unintended undefined values. One could imagine a similar control flow graph walk to ensure that every exiting control flow that passed through the creation of a particular closure has no subsequent assignments to a particular variable. It's possible to do, but it would definitely have significant performance effects.

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

TypeScript Bot (@typescript-bot) perf test this faster

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the tsc-only perf test suite on this PR at a490ef4. You can monitor the build here.

Update: The results are in!

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

TypeScript Bot (@typescript-bot) pack this

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the tarball bundle task on this PR at a490ef4. You can monitor the build here.

@typescript-bot

TypeScript Bot (typescript-bot) commented Dec 31, 2023

Copy link
Copy Markdown
Contributor

Hey Anders Hejlsberg (@ahejlsberg), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/159175/artifacts?artifactName=tgz&fileId=73C87748830547051A361376E39D0B51FE439C89A3DA965ABEFFB4C6076F529602&fileName=/typescript-5.4.0-insiders.20231231.tgz"
}
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.4.0-pr-56908-17".;

@typescript-bot

Copy link
Copy Markdown
Contributor

Anders Hejlsberg (@ahejlsberg)
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Angular - node (v18.15.0, x64)
Memory used295,443k (± 0.01%)295,482k (± 0.01%)~295,426k295,530kp=0.109 n=6
Parse Time2.65s (± 0.15%)2.65s (± 0.28%)~2.64s2.66sp=1.000 n=6
Bind Time0.82s (± 0.99%)0.82s (± 0.00%)~0.82s0.82sp=0.405 n=6
Check Time8.16s (± 0.24%)8.17s (± 0.29%)~8.14s8.20sp=0.569 n=6
Emit Time7.10s (± 0.19%)7.09s (± 0.27%)~7.07s7.12sp=0.510 n=6
Total Time18.73s (± 0.16%)18.73s (± 0.16%)~18.68s18.75sp=0.935 n=6
Compiler-Unions - node (v18.15.0, x64)
Memory used191,501k (± 0.02%)192,462k (± 1.25%)~191,440k197,374kp=0.810 n=6
Parse Time1.34s (± 0.99%)1.36s (± 1.72%)~1.32s1.39sp=0.250 n=6
Bind Time0.72s (± 0.00%)0.72s (± 0.00%)~0.72s0.72sp=1.000 n=6
Check Time9.28s (± 0.32%)9.30s (± 0.42%)~9.24s9.35sp=0.333 n=6
Emit Time2.62s (± 0.45%)2.62s (± 0.66%)~2.59s2.64sp=0.933 n=6
Total Time13.97s (± 0.21%)14.00s (± 0.29%)~13.95s14.06sp=0.109 n=6
Monaco - node (v18.15.0, x64)
Memory used347,405k (± 0.00%)347,406k (± 0.01%)~347,383k347,428kp=1.000 n=6
Parse Time2.46s (± 0.68%)2.46s (± 0.76%)~2.44s2.49sp=0.675 n=6
Bind Time0.92s (± 0.59%)0.92s (± 0.59%)~0.92s0.93sp=1.000 n=6
Check Time6.88s (± 0.40%)6.89s (± 0.52%)~6.86s6.94sp=0.746 n=6
Emit Time4.04s (± 0.40%)4.05s (± 0.40%)~4.03s4.08sp=0.121 n=6
Total Time14.31s (± 0.21%)14.33s (± 0.30%)~14.28s14.37sp=0.282 n=6
TFS - node (v18.15.0, x64)
Memory used302,726k (± 0.01%)302,743k (± 0.00%)~302,730k302,761kp=0.065 n=6
Parse Time1.99s (± 1.10%)2.00s (± 0.86%)~1.98s2.03sp=0.511 n=6
Bind Time1.00s (± 0.83%)1.00s (± 0.81%)~1.00s1.02sp=0.673 n=6
Check Time6.29s (± 0.73%)6.29s (± 0.31%)~6.26s6.31sp=0.466 n=6
Emit Time3.57s (± 0.50%)3.59s (± 0.78%)~3.57s3.64sp=0.139 n=6
Total Time12.86s (± 0.47%)12.89s (± 0.35%)~12.81s12.94sp=0.335 n=6
material-ui - node (v18.15.0, x64)
Memory used506,810k (± 0.01%)506,843k (± 0.00%)+33k (+ 0.01%)506,830k506,871kp=0.045 n=6
Parse Time2.60s (± 0.62%)2.58s (± 0.29%)-0.02s (- 0.77%)2.57s2.59sp=0.032 n=6
Bind Time0.99s (± 1.05%)1.00s (± 1.21%)~0.98s1.01sp=0.868 n=6
Check Time16.91s (± 0.32%)16.96s (± 0.38%)~16.89s17.08sp=0.227 n=6
Emit Time0.00s (± 0.00%)0.00s (± 0.00%)~0.00s0.00sp=1.000 n=6
Total Time20.51s (± 0.31%)20.54s (± 0.34%)~20.44s20.65sp=0.521 n=6
xstate - node (v18.15.0, x64)
Memory used512,877k (± 0.01%)512,951k (± 0.01%)+74k (+ 0.01%)512,874k513,024kp=0.045 n=6
Parse Time3.27s (± 0.39%)3.27s (± 0.19%)~3.26s3.28sp=0.799 n=6
Bind Time1.54s (± 0.41%)1.54s (± 0.34%)~1.53s1.54sp=0.386 n=6
Check Time2.82s (± 0.52%)2.83s (± 0.63%)~2.80s2.85sp=0.370 n=6
Emit Time0.07s (± 0.00%)0.07s (± 5.69%)~0.07s0.08sp=0.405 n=6
Total Time7.70s (± 0.14%)7.72s (± 0.23%)~7.69s7.74sp=0.107 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Angular - node (v18.15.0, x64)
  • Compiler-Unions - node (v18.15.0, x64)
  • Monaco - node (v18.15.0, x64)
  • TFS - node (v18.15.0, x64)
  • material-ui - node (v18.15.0, x64)
  • xstate - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) perf test public

@typescript-bot

TypeScript Bot (typescript-bot) commented Jan 8, 2024

Copy link
Copy Markdown
Contributor

Heya Jake Bailey (@jakebailey), I've started to run the public perf test suite on this PR at cd9b30a. You can monitor the build here.

Update: The results are in!

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey), the perf run you requested failed. You can check the log here.

@jakebailey

Copy link
Copy Markdown
Member

This PR appears to conflict with main and needs a merge and fix:

src/compiler/checker.ts:479:5 - error TS2305: Module '"./_namespaces/ts"' has no exported member 'isCatchClauseVariableDeclaration'.
479 isCatchClauseVariableDeclaration,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 1 error.

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) perf test public

@typescript-bot

TypeScript Bot (typescript-bot) commented Jan 9, 2024

Copy link
Copy Markdown
Contributor

Heya Jake Bailey (@jakebailey), I've started to run the public perf test suite on this PR at 28da1c5. You can monitor the build here.

Update: The results are in!

case SyntaxKind.SwitchStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.ClassDeclaration:
pos = node.end;

@DanielRosenwasserDaniel Rosenwasser (DanielRosenwasser)Jan 9, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if it's worth noting that LabeledStatement isn't covered here. I don't think that it really matters because its end position should be identical to any of the other cases.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, no special handling needed for LabeledStatement.

@DanielRosenwasser

Copy link
Copy Markdown
Member

Let's wait for the perf results before merging.

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
self-build-src - node (v20.5.1, x64)
Memory used2,573,421k (± 0.01%)2,681,290k (± 5.13%)🔻+107,869k (+ 4.19%)2,573,861k2,921,130kp=0.005 n=6
Parse Time5.00s (± 0.48%)5.02s (± 0.99%)~4.96s5.07sp=0.575 n=6
Bind Time1.99s (± 0.45%)1.99s (± 0.83%)~1.97s2.00sp=0.462 n=6
Check Time32.11s (± 0.22%)31.97s (± 0.47%)~31.75s32.15sp=0.199 n=6
Emit Time2.83s (± 1.89%)2.82s (± 3.98%)~2.67s2.96sp=0.810 n=6
Total Time41.95s (± 0.31%)41.81s (± 0.20%)~41.73s41.92sp=0.065 n=6
self-compiler - node (v20.5.1, x64)
Memory used418,760k (± 0.02%)418,817k (± 0.01%)~418,749k418,880kp=0.471 n=6
Parse Time2.88s (± 1.09%)2.88s (± 0.38%)~2.87s2.90sp=0.620 n=6
Bind Time1.14s (± 1.03%)1.14s (± 0.36%)~1.13s1.14sp=0.584 n=6
Check Time14.09s (± 0.34%)14.07s (± 0.38%)~13.99s14.14sp=0.748 n=6
Emit Time1.05s (± 1.16%)1.05s (± 1.84%)~1.03s1.08sp=0.797 n=6
Total Time19.15s (± 0.27%)19.14s (± 0.33%)~19.04s19.22sp=0.688 n=6
vscode - node (v20.5.1, x64)
Memory used2,827,969k (± 0.00%)2,828,011k (± 0.00%)~2,827,971k2,828,065kp=0.128 n=6
Parse Time10.72s (± 0.15%)10.73s (± 0.15%)~10.71s10.75sp=0.683 n=6
Bind Time3.42s (± 0.57%)3.42s (± 0.52%)~3.39s3.44sp=0.935 n=6
Check Time56.07s (± 0.36%)56.19s (± 0.25%)~56.08s56.43sp=0.261 n=6
Emit Time16.22s (± 0.52%)16.12s (± 0.81%)~15.91s16.32sp=0.170 n=6
Total Time86.44s (± 0.32%)86.46s (± 0.31%)~86.15s86.93sp=0.873 n=6
System info unknown
Hosts
  • node (v20.5.1, x64)
Scenarios
  • self-build-src - node (v20.5.1, x64)
  • self-compiler - node (v20.5.1, x64)
  • vscode - node (v20.5.1, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@ahejlsberg

Copy link
Copy Markdown
MemberAuthor

Performance is unaffected. We're good to merge.

@nicolo-ribaudo

Copy link
Copy Markdown
Contributor

Coming here from the twitter thread :)

Type refinements are not preserved in inner function and class declarations (due to hoisting):

Class declarations are not hoisted, so this improvement could also applied to them. The only hoisted values are function declarations.

@Methuselah96

Nathan Bierema (Methuselah96) commented Jan 9, 2024

Copy link
Copy Markdown

Already discussed here (with this response):

Note that class declarations are not hoisted, so we could actually remove this restriction if we wanted to.

It isn't a new restriction, it's actually been there since the dawn of control flow analysis in the compiler. For example, with or without this PR:

functionfoo(x: string|number){if(typeofx==="string"){x;// stringclassC{bar(){x}// string | number}}}

I was wondering what the reason is, but didn't want to change in this PR without double checking. I get that declared classes are only half-hoisted (as in, subject to TDZ checks), but might there be other reasons we've had this restriction all along?

Matt Bierner (mjbvz) pushed a commit to mjbvz/vscode that referenced this pull request Jan 11, 2024
With microsoft/TypeScript#56908, TS should better preserve type refinements. To help test this out, I made a quick pass through our code to remove type assertions that are no longer needed
Most of these are not impacted by microsoft/TypeScript#56908 but removing them helps TS test changes like this
Not adding an eslint rule for now as it requires whole program intellisense, which is too slow for commit hooks
Matt Bierner (mjbvz) added a commit to microsoft/vscode that referenced this pull request Jan 30, 2024
* Remove extra not null assertions
With microsoft/TypeScript#56908, TS should better preserve type refinements. To help test this out, I made a quick pass through our code to remove type assertions that are no longer needed
Most of these are not impacted by microsoft/TypeScript#56908 but removing them helps TS test changes like this
Not adding an eslint rule for now as it requires whole program intellisense, which is too slow for commit hooks
* Fix merge

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope this works

@boi-network12

The change with the latest commits is where type refinements for mutable global variables in closures are no longer preserved. This adjustment is made because such global variables may be altered by code in other files or modules.

The issue with narrowing x.a is a separate concern. While narrowing discriminated unions is technically unsafe due to the potential mutation of discriminants (which are mutable), such mutations are uncommon in practice. Despite this, the benefits of narrowing are significant and desirable.

@RyanCavanaugh

Copy link
Copy Markdown
Member

ChatGPT alert 🙄

@microsoftMicrosoft (microsoft) locked as resolved and limited conversation to collaborators Oct 16, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.