microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99.06k stars 12.29k forks source link

Experiment with tracking which entities actually may be narrowed #51525

Open DanielRosenwasser opened 1 year ago

DanielRosenwasser commented 1 year ago

In Pyright, narrowing can be avoided by keeping track of which entities actually get narrowed within a given scope. This could save a good amount of time by avoiding redundant walks up the control flow graph just to discover nothing interesting a given variable or property.

I'm not sure if Pyright also tracks the first location at which an entity may be narrowed, but that information could also be used to signal when a narrowing walk needs to stop.

This would come at the cost of some memory overhead, but we'd need to experiment to see what the trade-offs are.

jakebailey commented 1 year ago

I think the code in question for pyright was introduced in:

fatcerberus commented 1 year ago

Wait, does TS walk the control flow graph backwards to discover narrowings? I always figured the graph looked something like x = string | number -> if (true) x = string -> else x = number and the compiler would know exactly which part of the tree it was in at the time it encountered a reference to x so it could just look up the current type directly.

DanielRosenwasser commented 1 year ago

It does walk backwards. If it walked forwards, it would have to retain the context of all potential narrowing operations (and for all potential types we'd want to narrow from - which is occasionally necessary for things like narrowing constraints of generics rather than the generics themselves). That wouldn't work in our system where checking an expression should be as lazy as possible.

Anyway, in line with that, there are definitely opportunities for us to optimize here.

fatcerberus commented 1 year ago

I guess that makes sense now that I think about it - there really aren't any "set the type to string" style narrowings - even typeof x === 'string' can narrow to never if the original type doesn't overlap with string. Otherwise you could probably do some kind of short-circuiting logic.