-
Notifications
You must be signed in to change notification settings - Fork 42
/
html-collection.ts
124 lines (106 loc) · 2.72 KB
/
html-collection.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import type { Element } from "./element.ts";
const HTMLCollectionFakeClass: any = (() => {
return class HTMLCollection {
constructor() {
throw new TypeError("Illegal constructor");
}
static [Symbol.hasInstance](value: any) {
return value.constructor === HTMLCollectionClass;
}
};
})();
export const HTMLCollectionMutatorSym = Symbol();
// We define the `HTMLCollection` inside a closure to ensure that its
// `.name === "HTMLCollection"` property stays intact, as we need to manipulate
// its prototype and completely change its TypeScript-recognized type.
const HTMLCollectionClass: any = (() => {
// @ts-ignore
class HTMLCollection extends Array<Element> {
// @ts-ignore
forEach(
cb: (node: Element, index: number, nodeList: Element[]) => void,
thisArg: HTMLCollection | undefined = undefined,
) {
super.forEach(cb, thisArg);
}
item(index: number): Element | null {
return this[index] ?? null;
}
[HTMLCollectionMutatorSym]() {
return {
push: Array.prototype.push.bind(this),
splice: Array.prototype.splice.bind(this),
indexOf: Array.prototype.indexOf.bind(this),
};
}
toString() {
return "[object HTMLCollection]";
}
}
return HTMLCollection;
})();
for (
const staticMethod of [
"from",
"isArray",
"of",
]
) {
HTMLCollectionClass[staticMethod] = undefined;
}
for (
const instanceMethod of [
"concat",
"copyWithin",
"every",
"fill",
"filter",
"find",
"findIndex",
"flat",
"flatMap",
"includes",
"indexOf",
"join",
"lastIndexOf",
"map",
"pop",
"push",
"reduce",
"reduceRight",
"reverse",
"shift",
"slice",
"some",
"sort",
"splice",
"toLocaleString",
"unshift",
// Unlike NodeList, HTMLCollection also doesn't implement these
"entries",
"forEach",
"keys",
"values",
]
) {
HTMLCollectionClass.prototype[instanceMethod] = undefined;
}
export interface HTMLCollection {
new (): HTMLCollection;
readonly [index: number]: Element;
readonly length: number;
[Symbol.iterator](): Generator<Element>;
item(index: number): Element;
[HTMLCollectionMutatorSym](): HTMLCollectionMutator;
}
export interface HTMLCollectionPublic extends HTMLCollection {
[HTMLCollectionMutatorSym]: never;
}
export interface HTMLCollectionMutator {
push(...elements: Element[]): number;
splice(start: number, deleteCount?: number, ...items: Element[]): Element[];
indexOf(element: Element, fromIndex?: number | undefined): number;
}
export const HTMLCollection = <HTMLCollection> HTMLCollectionClass;
export const HTMLCollectionPublic =
<HTMLCollectionPublic> HTMLCollectionFakeClass;