-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Optimize creation of intersections of union types #25859
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
src/compiler/checker.ts
Outdated
|
||
// Remove all unions of primitive types from the given list and replace them with a | ||
// single union containing an intersection of those primitive types. | ||
function intersectUnionsOfPrimitiveTypes(types: Type[]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When does this return true
/false
? Can you make this explicit?
function eachUnionContains(unionTypes: UnionType[], type: Type) { | ||
for (const u of unionTypes) { | ||
if (!containsType(u.types, type)) { | ||
const primitive = type.flags & TypeFlags.StringLiteral ? stringType : |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is very close to getBaseTypeOfLiteralType
; can you use that instead? That also covers enums, though it won't come into play for the motivating issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although a 200 member enum could easily cause the same issues, so is probably worth covering!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, we can't use that instead because it maps an enum literal type to its defining union type (the union of all enum literal types in the same enum type declaration) which we don't want here. We only want to handle the special "infinite sets" denoted by string
, number
, and symbol
.
const result: Type[] = []; | ||
for (const u of unionTypes) { | ||
for (const t of u.types) { | ||
if (insertType(checked, t)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the worst-case, this can force checked
to reallocate or bulk-move all the elements a lot. Do we believe that won't be an issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could potentially use a Map
but that's going to allocate more memory and go slower in the typical cases (i.e. reasonably sized sets of types with a high degree of similarity). I'm not too concerned with it.
This PR optimizes creation of intersections of union types containing both unit types (such as string and numeric literal types) and primitive types (such as
string
andnumber
). This fixes the severe performance issues we have whenkeyof
is applied to large unions of object types that have string and/or numeric index signatures. (Given a union typeU
with constituentsT0 | T1 | ... | Tn
, we rewritekeyof U
tokeyof T0 & keyof T1 & ... & keyof Tn
. Thus it is important to have efficient resolution of such types.)Fixes #24223.