• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { getObservableTarget } from "./observable"
17
18/*
19    When decorating variables of complex types,
20    @Prop makes a deep copy, during which all types,
21    except primitive types, Map, Set, Date, and Array, will be lost.
22 */
23
24export function propDeepCopy<T>(sourceObject: T): T {
25    if (!sourceObject || typeof sourceObject !== 'object') {
26        return sourceObject
27    }
28
29    const copiedObjects = new Map<Object, Object>()
30    return recursiveDeepCopy(sourceObject) as T
31
32    function recursiveDeepCopy(sourceObject: Object): Object {
33        if (!sourceObject || typeof sourceObject !== 'object') {
34            return sourceObject
35        }
36
37        const storedObject = copiedObjects.get(sourceObject)
38        if (storedObject !== undefined) {
39            return storedObject
40        }
41
42        const copy: any = copyDeepTrackable(sourceObject)
43
44        const objectToCopyFrom = getObservableTarget(sourceObject)
45        Object.keys(objectToCopyFrom)
46            .forEach((key) => {
47                const property = objectToCopyFrom[key as keyof Object]
48
49                if (typeof property === "function") {
50                    Reflect.set(copy, key, property)
51                    copy[key] = copy[key].bind(copy)
52                    return
53                }
54                Reflect.set(copy, key, recursiveDeepCopy(property));
55            })
56
57        return copy
58    }
59
60    function copyDeepTrackable<T>(sourceObject: T): T {
61        if (sourceObject instanceof Set) {
62            const copy = new Set<any>()
63            Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject))
64            copiedObjects.set(sourceObject, copy)
65            for (const setKey of sourceObject.keys()) {
66                copy.add(recursiveDeepCopy(setKey))
67            }
68            return copy as T
69        }
70        if (sourceObject instanceof Map) {
71            const copy = new Map<any, any>()
72            Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject))
73            copiedObjects.set(sourceObject, copy)
74            for (const mapKey of sourceObject.keys()) {
75                copy.set(mapKey, recursiveDeepCopy(sourceObject.get(mapKey)))
76            }
77            return copy as T
78        }
79        if (sourceObject instanceof Date) {
80            const copy = new Date()
81            copy.setTime(sourceObject.getTime())
82            Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject))
83            copiedObjects.set(sourceObject, copy)
84            return copy as T
85        }
86        if (sourceObject instanceof Object) {
87            const copy = Array.isArray(sourceObject) ? [] : {}
88            Object.setPrototypeOf(copy, Object.getPrototypeOf(sourceObject))
89            copiedObjects.set(sourceObject, copy)
90            return copy as T
91        }
92
93        return sourceObject
94    }
95}
96