Smarter Literal/Subtype Reduction
#42353
- Imagine
EnumA and EnumB which each have 1000 members.
- If you have an array like
[EnumA.Member1, EnumB.Member1, someObject], then we'll try to do subtype reduction.
- Each enum is technically a union, with 1000 literal type members.
- Subtype reduction is O(n2), so when we try to reduce each enum, that can end up being expensive! We have a heuristic to avoid doing this work by checking up-front if we're going to do a lot of work.
- However, in this PR, we always start out by doing a literal type reduction pass (a pass which is way cheaper than general subtype reduction), and then do full subtype reduction.
- We always had a literal type reduction pass, but now we always apply it in the presence of any literal types (not just all-literal types).
- We also had this other special code to handle enums before; seems like we were able to remove them.
- The code got smaller from this change! 🎉
- Some...breaks.
- We used to keep
void | undefined - now we reduce it to undefined.
- A change where we now do not reduce primitives and object types that have members in common..
- For example,
[someString, { toString(): string { return "hi" } }] doesn't reduce to just the object type.
- Does it make sense that
undefined is reduced to void? Isn't it the other way around?
- We've always had
void being a supertype of undefined.
- Users try to cast to
unknown[] or any[] or whatever, but it doesn't stop the complex computation. Could we peek at that?
- Potentially - could just do a single assignability check to
Exploding Template String Types
`${theme.colors.grey} ${theme.colors.grey} ${theme.colors.white}`
declare var a: "a" | "whole" | "bunch" | "of" | ... | "strings";
const t = `${a} ${a} ${a}`;
-
Expands out to a very big union type.
-
If the union is too big, it can be too complex.
-
It used to be that you had to opt into template literal types with as const on template strings - now you're sort of always thrown into it.
-
Two proposals
- One is "this thing is too big" and give a better error message, and
- make it so that
as string allows you to opt out.
-
Do we have any tests for perf?
- Relatively new feature.
- Well we know this is strictly never going to be faster.
-
Try to find some project that uses template strings to see what sort of regressions they have.
- Artsy has some, we can ping them!
-
Also maybe provide a quick fix.*
-
Feels like the usual expectation is "give me a reasonable type, and I'll use as const unless I opt-in."
-
Can convince ourselves of either direction.
-
Thing that's odd about not doing this work
const x = `somestring`; // this is a literal type
const str = "string;
const x = `some${str}`; // this is not? fishy.
-
We really should try to avoid doing the full type computation if you're contextually typed, it happens all the time.
Smarter Literal/Subtype Reduction
#42353
EnumAandEnumBwhich each have 1000 members.[EnumA.Member1, EnumB.Member1, someObject], then we'll try to do subtype reduction.void | undefined- now we reduce it toundefined.[someString, { toString(): string { return "hi" } }]doesn't reduce to just the object type.undefinedis reduced tovoid? Isn't it the other way around?voidbeing a supertype ofundefined.unknown[]orany[]or whatever, but it doesn't stop the complex computation. Could we peek at that?Exploding Template String Types
`${theme.colors.grey} ${theme.colors.grey} ${theme.colors.white}`Expands out to a very big union type.
If the union is too big, it can be too complex.
It used to be that you had to opt into template literal types with
as conston template strings - now you're sort of always thrown into it.Two proposals
as stringallows you to opt out.Do we have any tests for perf?
Try to find some project that uses template strings to see what sort of regressions they have.
Also maybe provide a quick fix.*
Feels like the usual expectation is "give me a reasonable type, and I'll use
as constunless I opt-in."Can convince ourselves of either direction.
Thing that's odd about not doing this work
We really should try to avoid doing the full type computation if you're contextually typed, it happens all the time.