Ref #23977 and discussion on #24137
Code
Isolated repro (needs dom lib for ElementTagNameMap and associated types):
// @strict: true
// Modified repro from #23977
declare global {
interface ElementTagNameMap {
[index: number]: HTMLElement
}
interface HTMLElement {
[index: number]: HTMLElement;
}
}
export function assertIsElement(node: Node | null): node is Element {
let nodeType = node === null ? null : node.nodeType;
return nodeType === 1;
}
export function assertNodeTagName<
T extends keyof ElementTagNameMap,
U extends ElementTagNameMap[T]>(node: Node | null, tagName: T): node is U {
if (assertIsElement(node)) {
const nodeTagName = node.tagName.toLowerCase();
return nodeTagName === tagName;
}
return false;
}
export function assertNodeProperty<
T extends keyof ElementTagNameMap,
P extends keyof ElementTagNameMap[T],
V extends HTMLElementTagNameMap[T][P]>(node: Node | null, tagName: T, prop: P, value: V) {
if (assertNodeTagName(node, tagName)) {
node[prop];
}
}
Expected behavior:
No crash.
Actual behavior:
OOM crash after a very long delay.
Preliminary investigation shows the OOM occurs when we try to get the distributed form of keyof <Large Union> - namely the process is defined recursively, so when the union has ~170 members, we create 170 intermediate types (flattening one member at a time, while also wasting time recalculating type flags and other bits we will never need) before getting the final, distributed result. Now, picture doing this on every relation comparison operation for P (this distribution is uncached), and multiplying it for V in assertNodeProperty - the complexity is huge. Our fast path doesn't fix this, since the numeric index signatures cause us to bail on the fast path as it currently is.
Ref #23977 and discussion on #24137
Code
Isolated repro (needs
domlib forElementTagNameMapand associated types):Expected behavior:
No crash.
Actual behavior:
OOM crash after a very long delay.
Preliminary investigation shows the OOM occurs when we try to get the distributed form of
keyof <Large Union>- namely the process is defined recursively, so when the union has ~170 members, we create 170 intermediate types (flattening one member at a time, while also wasting time recalculating type flags and other bits we will never need) before getting the final, distributed result. Now, picture doing this on every relation comparison operation forP(this distribution is uncached), and multiplying it forVinassertNodeProperty- the complexity is huge. Our fast path doesn't fix this, since the numeric index signatures cause us to bail on the fast path as it currently is.