Description
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.