-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdeepCopy.ts
126 lines (90 loc) · 3.72 KB
/
deepCopy.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
/*
* Copyright (c) 2023 Michael Federczuk
* SPDX-License-Identifier: GPL-3.0-or-later
*/
import { deepFreeze } from "./deepFreeze";
import { canValueHaveProperties, getPropertyKeys } from "./_internal/utils";
const initCopy = (obj: NonNullable<object>): NonNullable<object> => {
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects>
// these objects all seem to have some special built-in property that cannot be copied over after creation, so we
// need to have special cases to create them
if (obj instanceof Array) return new Array(obj.map(deepCopy));
if (obj instanceof RegExp) return new RegExp(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof Map) {
const copiedEntries: [unknown, unknown][] = [];
for (const [key, value] of obj.entries()) {
copiedEntries.push([deepCopy(key), deepCopy(value)]);
}
return new Map(copiedEntries);
}
if (obj instanceof Set) {
const copiedValues: unknown[] = [];
for (const value of obj.values()) {
copiedValues.push(deepCopy(value));
}
return new Set(copiedValues);
}
//#region scalar arrays
if (obj instanceof Int8Array) return new Int8Array(obj);
if (obj instanceof Uint8Array) return new Uint8Array(obj);
if (obj instanceof Uint8ClampedArray) return new Uint8ClampedArray(obj);
if (obj instanceof Int16Array) return new Int16Array(obj);
if (obj instanceof Uint16Array) return new Uint16Array(obj);
if (obj instanceof Int32Array) return new Int32Array(obj);
if (obj instanceof Uint32Array) return new Uint32Array(obj);
if (obj instanceof Float32Array) return new Float32Array(obj);
if (obj instanceof Float64Array) return new Float64Array(obj);
if (obj instanceof BigInt64Array) return new BigInt64Array(obj);
if (obj instanceof BigUint64Array) return new BigUint64Array(obj);
//#endregion
if (obj instanceof ArrayBuffer) {
const newBuffer = new ArrayBuffer(obj.byteLength);
const origView = new Uint8Array(obj);
const newView = new Uint8Array(newBuffer);
newView.set(origView);
return newBuffer;
}
//#region
if (obj instanceof WeakMap) throw new TypeError("WeakMap objects cannot be copied");
if (obj instanceof WeakSet) throw new TypeError("WeakSet objects cannot be copied");
if (obj instanceof SharedArrayBuffer) throw new TypeError("SharedArrayBuffer objects cannot be copied");
if (obj instanceof DataView) throw new TypeError("DataView objects cannot be copied");
if (obj instanceof Promise) throw new TypeError("Promise objects cannot be copied");
//#endregion
return Object.create(obj);
};
deepFreeze(initCopy);
/**
* Creates a deep copy of **obj**.
*
* @param obj The object to create a deep copy of.
*
* @returns A deep copy of **obj**.
*/
export function deepCopy<T>(obj: T): T {
if (!(canValueHaveProperties(obj))) {
return obj;
}
if (typeof obj === "function") {
throw new TypeError("Function objects cannot be copied");
}
const copy: NonNullable<object> = initCopy(obj);
for (const propertyKey of getPropertyKeys(obj)) {
const propertyDescriptor: PropertyDescriptor =
(Object.getOwnPropertyDescriptor(obj, propertyKey) as PropertyDescriptor);
if ("value" in propertyDescriptor) {
propertyDescriptor.value = deepCopy(propertyDescriptor.value);
}
Object.defineProperty(copy, propertyKey, propertyDescriptor);
}
// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf>
// Mozilla recommends not using Object.setPrototypeOf for performance reasons, so we put at least put it behind a
// conditional
const objPrototype: (object | null) = Object.getPrototypeOf(obj);
if (Object.getPrototypeOf(copy) !== objPrototype) {
Object.setPrototypeOf(copy, objPrototype);
}
return (copy as T);
}
deepFreeze(deepCopy);