-
Notifications
You must be signed in to change notification settings - Fork 7
/
index.ts
134 lines (103 loc) · 2.88 KB
/
index.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
125
126
127
128
129
130
131
132
133
134
const generateId = (function () {
let uniqueNumber = 1;
const prefix = "__unique-";
return () => prefix + uniqueNumber++;
})();
const SYM: unique symbol = Symbol.for("__unique");
interface ISet {
get(element: unknown): unknown | undefined;
has(element: unknown): boolean;
add(element: unknown): void;
forEach(callback: (element: unknown) => void): void;
}
type TSetItems = {
[key: TSetItemKey]: unknown;
};
type TSetItem = { [SYM]?: string };
type TPrimitive =
| string
| number
| bigint
| symbol
| boolean
| null
| undefined;
type TSetItemKey = string | symbol;
const isSetItemKey = (key: unknown): key is TSetItemKey => {
const keyType = typeof key;
return keyType === "string" || keyType === "symbol";
};
const isPrimitive = (key: unknown): key is TPrimitive => {
return !isObjectItem(key);
};
const isObjectItem = (element: unknown): element is TSetItem => {
return (
(typeof element === "object" && element !== null) ||
typeof element === "function"
);
};
class MySet implements ISet {
#items: TSetItems = {};
constructor(iterable?: Array<unknown>) {
iterable?.forEach((item) => {
this.add(item);
});
}
#getItemKey(element: unknown): TSetItemKey | symbol | undefined {
if (isObjectItem(element)) {
const target = element?.[SYM];
if (target === undefined) return undefined;
return Symbol.for(target);
}
if (isPrimitive(element)) {
if (isSetItemKey(element)) {
return element;
}
return Symbol.for(element as any);
}
return undefined;
}
has(element: unknown): boolean {
const key = this.#getItemKey(element);
if (key === undefined) return false;
return this.#items.hasOwnProperty(key);
}
add(element: unknown): void {
if (typeof element === "symbol")
throw Error("Symbol is not supported now.");
if (this.has(element)) return;
let currentKey: TSetItemKey = Symbol.for(element as any);
if (isSetItemKey(element)) {
currentKey = element;
}
if (isObjectItem(element)) {
const generatedKey = generateId();
Object.defineProperty(element, SYM, {
value: generatedKey,
});
currentKey = Symbol.for(generatedKey);
}
Object.defineProperty(this.#items, currentKey, {
value: element,
});
}
forEach(callback: (element: unknown) => void): void {
for (const value of Object.getOwnPropertySymbols(this.#items)) {
callback(this.#items[value]);
}
}
get(element: unknown) {
if (!this.has(element)) return undefined;
const key = this.#getItemKey(element);
if (!key) return undefined;
return this.#items[key];
}
*[Symbol.iterator]() {
for (const symbolKey of Object.getOwnPropertySymbols(this.#items)) {
yield this.#items[symbolKey];
}
for (const key of Object.getOwnPropertyNames(this.#items)) {
yield this.#items[key];
}
}
}