ts-data-forge 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/array/array-utils.d.mts +2617 -0
- package/dist/array/array-utils.d.mts.map +1 -0
- package/dist/array/array-utils.mjs +2915 -0
- package/dist/array/array-utils.mjs.map +1 -0
- package/dist/array/index.d.mts +3 -0
- package/dist/array/index.d.mts.map +1 -0
- package/dist/array/index.mjs +3 -0
- package/dist/array/index.mjs.map +1 -0
- package/dist/array/tuple-utils.d.mts +421 -0
- package/dist/array/tuple-utils.d.mts.map +1 -0
- package/dist/array/tuple-utils.mjs +391 -0
- package/dist/array/tuple-utils.mjs.map +1 -0
- package/dist/collections/imap-mapped.d.mts +445 -0
- package/dist/collections/imap-mapped.d.mts.map +1 -0
- package/dist/collections/imap-mapped.mjs +424 -0
- package/dist/collections/imap-mapped.mjs.map +1 -0
- package/dist/collections/imap.d.mts +359 -0
- package/dist/collections/imap.d.mts.map +1 -0
- package/dist/collections/imap.mjs +338 -0
- package/dist/collections/imap.mjs.map +1 -0
- package/dist/collections/index.d.mts +7 -0
- package/dist/collections/index.d.mts.map +1 -0
- package/dist/collections/index.mjs +7 -0
- package/dist/collections/index.mjs.map +1 -0
- package/dist/collections/iset-mapped.d.mts +576 -0
- package/dist/collections/iset-mapped.d.mts.map +1 -0
- package/dist/collections/iset-mapped.mjs +522 -0
- package/dist/collections/iset-mapped.mjs.map +1 -0
- package/dist/collections/iset.d.mts +426 -0
- package/dist/collections/iset.d.mts.map +1 -0
- package/dist/collections/iset.mjs +437 -0
- package/dist/collections/iset.mjs.map +1 -0
- package/dist/collections/queue.d.mts +190 -0
- package/dist/collections/queue.d.mts.map +1 -0
- package/dist/collections/queue.mjs +317 -0
- package/dist/collections/queue.mjs.map +1 -0
- package/dist/collections/stack.d.mts +210 -0
- package/dist/collections/stack.d.mts.map +1 -0
- package/dist/collections/stack.mjs +353 -0
- package/dist/collections/stack.mjs.map +1 -0
- package/dist/expect-type.d.mts +199 -0
- package/dist/expect-type.d.mts.map +1 -0
- package/dist/expect-type.mjs +201 -0
- package/dist/expect-type.mjs.map +1 -0
- package/dist/functional/index.d.mts +5 -0
- package/dist/functional/index.d.mts.map +1 -0
- package/dist/functional/index.mjs +5 -0
- package/dist/functional/index.mjs.map +1 -0
- package/dist/functional/match.d.mts +215 -0
- package/dist/functional/match.d.mts.map +1 -0
- package/dist/functional/match.mjs +139 -0
- package/dist/functional/match.mjs.map +1 -0
- package/dist/functional/optional.d.mts +517 -0
- package/dist/functional/optional.d.mts.map +1 -0
- package/dist/functional/optional.mjs +532 -0
- package/dist/functional/optional.mjs.map +1 -0
- package/dist/functional/pipe.d.mts +185 -0
- package/dist/functional/pipe.d.mts.map +1 -0
- package/dist/functional/pipe.mjs +129 -0
- package/dist/functional/pipe.mjs.map +1 -0
- package/dist/functional/result.d.mts +796 -0
- package/dist/functional/result.d.mts.map +1 -0
- package/dist/functional/result.mjs +844 -0
- package/dist/functional/result.mjs.map +1 -0
- package/dist/globals.d.mts +38 -0
- package/dist/guard/has-key.d.mts +100 -0
- package/dist/guard/has-key.d.mts.map +1 -0
- package/dist/guard/has-key.mjs +94 -0
- package/dist/guard/has-key.mjs.map +1 -0
- package/dist/guard/index.d.mts +8 -0
- package/dist/guard/index.d.mts.map +1 -0
- package/dist/guard/index.mjs +8 -0
- package/dist/guard/index.mjs.map +1 -0
- package/dist/guard/is-non-empty-string.d.mts +106 -0
- package/dist/guard/is-non-empty-string.d.mts.map +1 -0
- package/dist/guard/is-non-empty-string.mjs +108 -0
- package/dist/guard/is-non-empty-string.mjs.map +1 -0
- package/dist/guard/is-non-null-object.d.mts +105 -0
- package/dist/guard/is-non-null-object.d.mts.map +1 -0
- package/dist/guard/is-non-null-object.mjs +108 -0
- package/dist/guard/is-non-null-object.mjs.map +1 -0
- package/dist/guard/is-primitive.d.mts +146 -0
- package/dist/guard/is-primitive.d.mts.map +1 -0
- package/dist/guard/is-primitive.mjs +161 -0
- package/dist/guard/is-primitive.mjs.map +1 -0
- package/dist/guard/is-record.d.mts +151 -0
- package/dist/guard/is-record.d.mts.map +1 -0
- package/dist/guard/is-record.mjs +155 -0
- package/dist/guard/is-record.mjs.map +1 -0
- package/dist/guard/is-type.d.mts +430 -0
- package/dist/guard/is-type.d.mts.map +1 -0
- package/dist/guard/is-type.mjs +432 -0
- package/dist/guard/is-type.mjs.map +1 -0
- package/dist/guard/key-is-in.d.mts +158 -0
- package/dist/guard/key-is-in.d.mts.map +1 -0
- package/dist/guard/key-is-in.mjs +160 -0
- package/dist/guard/key-is-in.mjs.map +1 -0
- package/dist/index.d.mts +11 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +61 -0
- package/dist/index.mjs.map +1 -0
- package/dist/iterator/index.d.mts +2 -0
- package/dist/iterator/index.d.mts.map +1 -0
- package/dist/iterator/index.mjs +2 -0
- package/dist/iterator/index.mjs.map +1 -0
- package/dist/iterator/range.d.mts +97 -0
- package/dist/iterator/range.d.mts.map +1 -0
- package/dist/iterator/range.mjs +130 -0
- package/dist/iterator/range.mjs.map +1 -0
- package/dist/json/index.d.mts +2 -0
- package/dist/json/index.d.mts.map +1 -0
- package/dist/json/index.mjs +2 -0
- package/dist/json/index.mjs.map +1 -0
- package/dist/json/json.d.mts +597 -0
- package/dist/json/json.d.mts.map +1 -0
- package/dist/json/json.mjs +687 -0
- package/dist/json/json.mjs.map +1 -0
- package/dist/number/branded-types/finite-number.d.mts +291 -0
- package/dist/number/branded-types/finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/finite-number.mjs +296 -0
- package/dist/number/branded-types/finite-number.mjs.map +1 -0
- package/dist/number/branded-types/index.d.mts +27 -0
- package/dist/number/branded-types/index.d.mts.map +1 -0
- package/dist/number/branded-types/index.mjs +27 -0
- package/dist/number/branded-types/index.mjs.map +1 -0
- package/dist/number/branded-types/int.d.mts +242 -0
- package/dist/number/branded-types/int.d.mts.map +1 -0
- package/dist/number/branded-types/int.mjs +239 -0
- package/dist/number/branded-types/int.mjs.map +1 -0
- package/dist/number/branded-types/int16.d.mts +162 -0
- package/dist/number/branded-types/int16.d.mts.map +1 -0
- package/dist/number/branded-types/int16.mjs +141 -0
- package/dist/number/branded-types/int16.mjs.map +1 -0
- package/dist/number/branded-types/int32.d.mts +155 -0
- package/dist/number/branded-types/int32.d.mts.map +1 -0
- package/dist/number/branded-types/int32.mjs +142 -0
- package/dist/number/branded-types/int32.mjs.map +1 -0
- package/dist/number/branded-types/non-negative-finite-number.d.mts +165 -0
- package/dist/number/branded-types/non-negative-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/non-negative-finite-number.mjs +160 -0
- package/dist/number/branded-types/non-negative-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/non-negative-int16.d.mts +160 -0
- package/dist/number/branded-types/non-negative-int16.d.mts.map +1 -0
- package/dist/number/branded-types/non-negative-int16.mjs +138 -0
- package/dist/number/branded-types/non-negative-int16.mjs.map +1 -0
- package/dist/number/branded-types/non-negative-int32.d.mts +156 -0
- package/dist/number/branded-types/non-negative-int32.d.mts.map +1 -0
- package/dist/number/branded-types/non-negative-int32.mjs +138 -0
- package/dist/number/branded-types/non-negative-int32.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-finite-number.d.mts +154 -0
- package/dist/number/branded-types/non-zero-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-finite-number.mjs +160 -0
- package/dist/number/branded-types/non-zero-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-int.d.mts +131 -0
- package/dist/number/branded-types/non-zero-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-int.mjs +128 -0
- package/dist/number/branded-types/non-zero-int.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-int16.d.mts +166 -0
- package/dist/number/branded-types/non-zero-int16.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-int16.mjs +145 -0
- package/dist/number/branded-types/non-zero-int16.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-int32.d.mts +158 -0
- package/dist/number/branded-types/non-zero-int32.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-int32.mjs +145 -0
- package/dist/number/branded-types/non-zero-int32.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-safe-int.d.mts +148 -0
- package/dist/number/branded-types/non-zero-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-safe-int.mjs +145 -0
- package/dist/number/branded-types/non-zero-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-uint16.d.mts +160 -0
- package/dist/number/branded-types/non-zero-uint16.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-uint16.mjs +140 -0
- package/dist/number/branded-types/non-zero-uint16.mjs.map +1 -0
- package/dist/number/branded-types/non-zero-uint32.d.mts +156 -0
- package/dist/number/branded-types/non-zero-uint32.d.mts.map +1 -0
- package/dist/number/branded-types/non-zero-uint32.mjs +140 -0
- package/dist/number/branded-types/non-zero-uint32.mjs.map +1 -0
- package/dist/number/branded-types/positive-finite-number.d.mts +171 -0
- package/dist/number/branded-types/positive-finite-number.d.mts.map +1 -0
- package/dist/number/branded-types/positive-finite-number.mjs +165 -0
- package/dist/number/branded-types/positive-finite-number.mjs.map +1 -0
- package/dist/number/branded-types/positive-int.d.mts +270 -0
- package/dist/number/branded-types/positive-int.d.mts.map +1 -0
- package/dist/number/branded-types/positive-int.mjs +257 -0
- package/dist/number/branded-types/positive-int.mjs.map +1 -0
- package/dist/number/branded-types/positive-int16.d.mts +162 -0
- package/dist/number/branded-types/positive-int16.d.mts.map +1 -0
- package/dist/number/branded-types/positive-int16.mjs +139 -0
- package/dist/number/branded-types/positive-int16.mjs.map +1 -0
- package/dist/number/branded-types/positive-int32.d.mts +158 -0
- package/dist/number/branded-types/positive-int32.d.mts.map +1 -0
- package/dist/number/branded-types/positive-int32.mjs +139 -0
- package/dist/number/branded-types/positive-int32.mjs.map +1 -0
- package/dist/number/branded-types/positive-safe-int.d.mts +152 -0
- package/dist/number/branded-types/positive-safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/positive-safe-int.mjs +138 -0
- package/dist/number/branded-types/positive-safe-int.mjs.map +1 -0
- package/dist/number/branded-types/positive-uint16.d.mts +160 -0
- package/dist/number/branded-types/positive-uint16.d.mts.map +1 -0
- package/dist/number/branded-types/positive-uint16.mjs +139 -0
- package/dist/number/branded-types/positive-uint16.mjs.map +1 -0
- package/dist/number/branded-types/positive-uint32.d.mts +156 -0
- package/dist/number/branded-types/positive-uint32.d.mts.map +1 -0
- package/dist/number/branded-types/positive-uint32.mjs +139 -0
- package/dist/number/branded-types/positive-uint32.mjs.map +1 -0
- package/dist/number/branded-types/safe-int.d.mts +243 -0
- package/dist/number/branded-types/safe-int.d.mts.map +1 -0
- package/dist/number/branded-types/safe-int.mjs +240 -0
- package/dist/number/branded-types/safe-int.mjs.map +1 -0
- package/dist/number/branded-types/safe-uint.d.mts +151 -0
- package/dist/number/branded-types/safe-uint.d.mts.map +1 -0
- package/dist/number/branded-types/safe-uint.mjs +138 -0
- package/dist/number/branded-types/safe-uint.mjs.map +1 -0
- package/dist/number/branded-types/uint.d.mts +144 -0
- package/dist/number/branded-types/uint.d.mts.map +1 -0
- package/dist/number/branded-types/uint.mjs +132 -0
- package/dist/number/branded-types/uint.mjs.map +1 -0
- package/dist/number/branded-types/uint16.d.mts +157 -0
- package/dist/number/branded-types/uint16.d.mts.map +1 -0
- package/dist/number/branded-types/uint16.mjs +137 -0
- package/dist/number/branded-types/uint16.mjs.map +1 -0
- package/dist/number/branded-types/uint32.d.mts +185 -0
- package/dist/number/branded-types/uint32.d.mts.map +1 -0
- package/dist/number/branded-types/uint32.mjs +169 -0
- package/dist/number/branded-types/uint32.mjs.map +1 -0
- package/dist/number/enum/index.d.mts +3 -0
- package/dist/number/enum/index.d.mts.map +1 -0
- package/dist/number/enum/index.mjs +3 -0
- package/dist/number/enum/index.mjs.map +1 -0
- package/dist/number/enum/int8.d.mts +202 -0
- package/dist/number/enum/int8.d.mts.map +1 -0
- package/dist/number/enum/int8.mjs +296 -0
- package/dist/number/enum/int8.mjs.map +1 -0
- package/dist/number/enum/uint8.d.mts +128 -0
- package/dist/number/enum/uint8.d.mts.map +1 -0
- package/dist/number/enum/uint8.mjs +251 -0
- package/dist/number/enum/uint8.mjs.map +1 -0
- package/dist/number/index.d.mts +5 -0
- package/dist/number/index.d.mts.map +1 -0
- package/dist/number/index.mjs +31 -0
- package/dist/number/index.mjs.map +1 -0
- package/dist/number/num.d.mts +515 -0
- package/dist/number/num.d.mts.map +1 -0
- package/dist/number/num.mjs +513 -0
- package/dist/number/num.mjs.map +1 -0
- package/dist/number/refined-number-utils.d.mts +191 -0
- package/dist/number/refined-number-utils.d.mts.map +1 -0
- package/dist/number/refined-number-utils.mjs +179 -0
- package/dist/number/refined-number-utils.mjs.map +1 -0
- package/dist/object/index.d.mts +2 -0
- package/dist/object/index.d.mts.map +1 -0
- package/dist/object/index.mjs +2 -0
- package/dist/object/index.mjs.map +1 -0
- package/dist/object/object.d.mts +296 -0
- package/dist/object/object.d.mts.map +1 -0
- package/dist/object/object.mjs +295 -0
- package/dist/object/object.mjs.map +1 -0
- package/dist/others/cast-mutable.d.mts +110 -0
- package/dist/others/cast-mutable.d.mts.map +1 -0
- package/dist/others/cast-mutable.mjs +114 -0
- package/dist/others/cast-mutable.mjs.map +1 -0
- package/dist/others/cast-readonly.d.mts +189 -0
- package/dist/others/cast-readonly.d.mts.map +1 -0
- package/dist/others/cast-readonly.mjs +193 -0
- package/dist/others/cast-readonly.mjs.map +1 -0
- package/dist/others/if-then.d.mts +98 -0
- package/dist/others/if-then.d.mts.map +1 -0
- package/dist/others/if-then.mjs +100 -0
- package/dist/others/if-then.mjs.map +1 -0
- package/dist/others/index.d.mts +8 -0
- package/dist/others/index.d.mts.map +1 -0
- package/dist/others/index.mjs +8 -0
- package/dist/others/index.mjs.map +1 -0
- package/dist/others/map-nullable.d.mts +151 -0
- package/dist/others/map-nullable.d.mts.map +1 -0
- package/dist/others/map-nullable.mjs +159 -0
- package/dist/others/map-nullable.mjs.map +1 -0
- package/dist/others/memoize-function.d.mts +173 -0
- package/dist/others/memoize-function.d.mts.map +1 -0
- package/dist/others/memoize-function.mjs +189 -0
- package/dist/others/memoize-function.mjs.map +1 -0
- package/dist/others/tuple.d.mts +159 -0
- package/dist/others/tuple.d.mts.map +1 -0
- package/dist/others/tuple.mjs +161 -0
- package/dist/others/tuple.mjs.map +1 -0
- package/dist/others/unknown-to-string.d.mts +180 -0
- package/dist/others/unknown-to-string.d.mts.map +1 -0
- package/dist/others/unknown-to-string.mjs +211 -0
- package/dist/others/unknown-to-string.mjs.map +1 -0
- package/dist/tsconfig.json +1 -0
- package/package.json +18 -16
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Casts a readonly type `T` to its `Mutable<T>` equivalent.
|
|
3
|
+
*
|
|
4
|
+
* **⚠️ Safety Warning**: This is a type assertion that bypasses TypeScript's immutability guarantees.
|
|
5
|
+
* The runtime value remains unchanged - only the type system's view of it changes.
|
|
6
|
+
* Use with caution as it can lead to unexpected mutations of data that was intended to be immutable.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the readonly value
|
|
9
|
+
* @param readonlyValue - The readonly value to cast to mutable
|
|
10
|
+
* @returns The same value with readonly modifiers removed from its type
|
|
11
|
+
*
|
|
12
|
+
* @example Basic usage with arrays and objects
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const readonlyArr: readonly number[] = [1, 2, 3];
|
|
15
|
+
* const mutableArr = castMutable(readonlyArr);
|
|
16
|
+
* mutableArr.push(4); // Now allowed by TypeScript
|
|
17
|
+
*
|
|
18
|
+
* const readonlyObj: { readonly x: number } = { x: 1 };
|
|
19
|
+
* const mutableObj = castMutable(readonlyObj);
|
|
20
|
+
* mutableObj.x = 2; // Now allowed by TypeScript
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example When to use - Working with third-party APIs
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Some APIs require mutable arrays but you have readonly data
|
|
26
|
+
* const readonlyData: readonly string[] = ['a', 'b', 'c'];
|
|
27
|
+
* const sortedData = castMutable([...readonlyData]); // Create a copy first!
|
|
28
|
+
* legacyApi.sort(sortedData); // API mutates the array
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Anti-pattern - Avoid mutating shared data
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // ❌ Bad: Mutating data that other code expects to be immutable
|
|
34
|
+
* const sharedConfig: Readonly<Config> = getConfig();
|
|
35
|
+
* const mutable = castMutable(sharedConfig);
|
|
36
|
+
* mutable.apiKey = 'new-key'; // Dangerous! Other code expects this to be immutable
|
|
37
|
+
*
|
|
38
|
+
* // ✅ Good: Create a copy if you need to mutate
|
|
39
|
+
* const configCopy = castMutable({ ...sharedConfig });
|
|
40
|
+
* configCopy.apiKey = 'new-key'; // Safe - operating on a copy
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @see castDeepMutable - For deeply nested readonly structures
|
|
44
|
+
* @see castReadonly - For the opposite operation
|
|
45
|
+
*/
|
|
46
|
+
export declare const castMutable: <T>(readonlyValue: T) => Mutable<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Casts a readonly type `T` to its `DeepMutable<T>` equivalent, recursively removing all readonly modifiers.
|
|
49
|
+
*
|
|
50
|
+
* **⚠️ Safety Warning**: This recursively bypasses ALL immutability guarantees in nested structures.
|
|
51
|
+
* Extremely dangerous for complex data structures as it allows mutation at any depth.
|
|
52
|
+
* The runtime value is unchanged - only TypeScript's type checking is affected.
|
|
53
|
+
*
|
|
54
|
+
* @template T - The type of the deeply readonly value
|
|
55
|
+
* @param readonlyValue - The deeply readonly value to cast to deeply mutable
|
|
56
|
+
* @returns The same value with all readonly modifiers recursively removed from its type
|
|
57
|
+
*
|
|
58
|
+
* @example Basic usage with nested structures
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const readonlyNested: {
|
|
61
|
+
* readonly a: { readonly b: readonly number[] }
|
|
62
|
+
* } = { a: { b: [1, 2, 3] } };
|
|
63
|
+
*
|
|
64
|
+
* const mutableNested = castDeepMutable(readonlyNested);
|
|
65
|
+
* mutableNested.a.b.push(4); // Mutations allowed at all levels
|
|
66
|
+
* mutableNested.a = { b: [5, 6] }; // Can replace entire objects
|
|
67
|
+
* mutableNested.a.b[0] = 99; // Can mutate array elements
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example Practical use case - Working with immutable state updates
|
|
71
|
+
* ```typescript
|
|
72
|
+
* // When you need to perform multiple mutations before creating new immutable state
|
|
73
|
+
* const currentState: DeepReadonly<AppState> = getState();
|
|
74
|
+
* const draft = castDeepMutable(structuredClone(currentState)); // Clone first!
|
|
75
|
+
*
|
|
76
|
+
* // Perform multiple mutations on the draft
|
|
77
|
+
* draft.users.push(newUser);
|
|
78
|
+
* draft.settings.theme = 'dark';
|
|
79
|
+
* draft.data.items[0].completed = true;
|
|
80
|
+
*
|
|
81
|
+
* // Create new immutable state from the mutated draft
|
|
82
|
+
* const newState = castDeepReadonly(draft);
|
|
83
|
+
* setState(newState);
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @example Type complexity with generics
|
|
87
|
+
* ```typescript
|
|
88
|
+
* type DeepReadonlyUser = DeepReadonly<{
|
|
89
|
+
* id: number;
|
|
90
|
+
* profile: {
|
|
91
|
+
* settings: {
|
|
92
|
+
* preferences: string[];
|
|
93
|
+
* };
|
|
94
|
+
* };
|
|
95
|
+
* }>;
|
|
96
|
+
*
|
|
97
|
+
* function updateUserPreferences(user: DeepReadonlyUser, newPref: string) {
|
|
98
|
+
* // Create a mutable copy to work with
|
|
99
|
+
* const mutableUser = castDeepMutable(structuredClone(user));
|
|
100
|
+
* mutableUser.profile.settings.preferences.push(newPref);
|
|
101
|
+
* return castDeepReadonly(mutableUser);
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @see castMutable - For shallow mutability casting
|
|
106
|
+
* @see castDeepReadonly - For the opposite operation
|
|
107
|
+
* @see structuredClone - Recommended for creating safe copies before mutation
|
|
108
|
+
*/
|
|
109
|
+
export declare const castDeepMutable: <T>(readonlyValue: T) => DeepMutable<T>;
|
|
110
|
+
//# sourceMappingURL=cast-mutable.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast-mutable.d.mts","sourceRoot":"","sources":["../../src/others/cast-mutable.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,eAAO,MAAM,WAAW,GAAI,CAAC,EAAG,eAAe,CAAC,KAAG,OAAO,CAAC,CAAC,CAC/B,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,EAAG,eAAe,CAAC,KAAG,WAAW,CAAC,CAAC,CAEnC,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Casts a readonly type `T` to its `Mutable<T>` equivalent.
|
|
3
|
+
*
|
|
4
|
+
* **⚠️ Safety Warning**: This is a type assertion that bypasses TypeScript's immutability guarantees.
|
|
5
|
+
* The runtime value remains unchanged - only the type system's view of it changes.
|
|
6
|
+
* Use with caution as it can lead to unexpected mutations of data that was intended to be immutable.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the readonly value
|
|
9
|
+
* @param readonlyValue - The readonly value to cast to mutable
|
|
10
|
+
* @returns The same value with readonly modifiers removed from its type
|
|
11
|
+
*
|
|
12
|
+
* @example Basic usage with arrays and objects
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const readonlyArr: readonly number[] = [1, 2, 3];
|
|
15
|
+
* const mutableArr = castMutable(readonlyArr);
|
|
16
|
+
* mutableArr.push(4); // Now allowed by TypeScript
|
|
17
|
+
*
|
|
18
|
+
* const readonlyObj: { readonly x: number } = { x: 1 };
|
|
19
|
+
* const mutableObj = castMutable(readonlyObj);
|
|
20
|
+
* mutableObj.x = 2; // Now allowed by TypeScript
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example When to use - Working with third-party APIs
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Some APIs require mutable arrays but you have readonly data
|
|
26
|
+
* const readonlyData: readonly string[] = ['a', 'b', 'c'];
|
|
27
|
+
* const sortedData = castMutable([...readonlyData]); // Create a copy first!
|
|
28
|
+
* legacyApi.sort(sortedData); // API mutates the array
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @example Anti-pattern - Avoid mutating shared data
|
|
32
|
+
* ```typescript
|
|
33
|
+
* // ❌ Bad: Mutating data that other code expects to be immutable
|
|
34
|
+
* const sharedConfig: Readonly<Config> = getConfig();
|
|
35
|
+
* const mutable = castMutable(sharedConfig);
|
|
36
|
+
* mutable.apiKey = 'new-key'; // Dangerous! Other code expects this to be immutable
|
|
37
|
+
*
|
|
38
|
+
* // ✅ Good: Create a copy if you need to mutate
|
|
39
|
+
* const configCopy = castMutable({ ...sharedConfig });
|
|
40
|
+
* configCopy.apiKey = 'new-key'; // Safe - operating on a copy
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @see castDeepMutable - For deeply nested readonly structures
|
|
44
|
+
* @see castReadonly - For the opposite operation
|
|
45
|
+
*/
|
|
46
|
+
const castMutable = (readonlyValue) => readonlyValue;
|
|
47
|
+
/**
|
|
48
|
+
* Casts a readonly type `T` to its `DeepMutable<T>` equivalent, recursively removing all readonly modifiers.
|
|
49
|
+
*
|
|
50
|
+
* **⚠️ Safety Warning**: This recursively bypasses ALL immutability guarantees in nested structures.
|
|
51
|
+
* Extremely dangerous for complex data structures as it allows mutation at any depth.
|
|
52
|
+
* The runtime value is unchanged - only TypeScript's type checking is affected.
|
|
53
|
+
*
|
|
54
|
+
* @template T - The type of the deeply readonly value
|
|
55
|
+
* @param readonlyValue - The deeply readonly value to cast to deeply mutable
|
|
56
|
+
* @returns The same value with all readonly modifiers recursively removed from its type
|
|
57
|
+
*
|
|
58
|
+
* @example Basic usage with nested structures
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const readonlyNested: {
|
|
61
|
+
* readonly a: { readonly b: readonly number[] }
|
|
62
|
+
* } = { a: { b: [1, 2, 3] } };
|
|
63
|
+
*
|
|
64
|
+
* const mutableNested = castDeepMutable(readonlyNested);
|
|
65
|
+
* mutableNested.a.b.push(4); // Mutations allowed at all levels
|
|
66
|
+
* mutableNested.a = { b: [5, 6] }; // Can replace entire objects
|
|
67
|
+
* mutableNested.a.b[0] = 99; // Can mutate array elements
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @example Practical use case - Working with immutable state updates
|
|
71
|
+
* ```typescript
|
|
72
|
+
* // When you need to perform multiple mutations before creating new immutable state
|
|
73
|
+
* const currentState: DeepReadonly<AppState> = getState();
|
|
74
|
+
* const draft = castDeepMutable(structuredClone(currentState)); // Clone first!
|
|
75
|
+
*
|
|
76
|
+
* // Perform multiple mutations on the draft
|
|
77
|
+
* draft.users.push(newUser);
|
|
78
|
+
* draft.settings.theme = 'dark';
|
|
79
|
+
* draft.data.items[0].completed = true;
|
|
80
|
+
*
|
|
81
|
+
* // Create new immutable state from the mutated draft
|
|
82
|
+
* const newState = castDeepReadonly(draft);
|
|
83
|
+
* setState(newState);
|
|
84
|
+
* ```
|
|
85
|
+
*
|
|
86
|
+
* @example Type complexity with generics
|
|
87
|
+
* ```typescript
|
|
88
|
+
* type DeepReadonlyUser = DeepReadonly<{
|
|
89
|
+
* id: number;
|
|
90
|
+
* profile: {
|
|
91
|
+
* settings: {
|
|
92
|
+
* preferences: string[];
|
|
93
|
+
* };
|
|
94
|
+
* };
|
|
95
|
+
* }>;
|
|
96
|
+
*
|
|
97
|
+
* function updateUserPreferences(user: DeepReadonlyUser, newPref: string) {
|
|
98
|
+
* // Create a mutable copy to work with
|
|
99
|
+
* const mutableUser = castDeepMutable(structuredClone(user));
|
|
100
|
+
* mutableUser.profile.settings.preferences.push(newPref);
|
|
101
|
+
* return castDeepReadonly(mutableUser);
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* @see castMutable - For shallow mutability casting
|
|
106
|
+
* @see castDeepReadonly - For the opposite operation
|
|
107
|
+
* @see structuredClone - Recommended for creating safe copies before mutation
|
|
108
|
+
*/
|
|
109
|
+
const castDeepMutable = (readonlyValue) =>
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
111
|
+
readonlyValue;
|
|
112
|
+
|
|
113
|
+
export { castDeepMutable, castMutable };
|
|
114
|
+
//# sourceMappingURL=cast-mutable.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast-mutable.mjs","sources":["../../src/others/cast-mutable.mts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CG;AACI,MAAM,WAAW,GAAG,CAAK,aAAgB,KAC9C;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DG;AACI,MAAM,eAAe,GAAG,CAAK,aAAgB;AAClD;AACA;;;;"}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Casts a mutable type `T` to its `Readonly<T>` equivalent.
|
|
3
|
+
*
|
|
4
|
+
* This is a safe type assertion that adds immutability constraints at the type level.
|
|
5
|
+
* The runtime value remains unchanged - only TypeScript's view of it becomes readonly.
|
|
6
|
+
* This helps prevent accidental mutations and makes code intentions clearer.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the mutable value
|
|
9
|
+
* @param mutable - The mutable value to cast to readonly
|
|
10
|
+
* @returns The same value with readonly modifiers added to its type
|
|
11
|
+
*
|
|
12
|
+
* @example Basic usage with arrays and objects
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const mutableArr: number[] = [1, 2, 3];
|
|
15
|
+
* const readonlyArr = castReadonly(mutableArr);
|
|
16
|
+
* // readonlyArr.push(4); // ❌ TypeScript Error: no 'push' on readonly array
|
|
17
|
+
*
|
|
18
|
+
* const mutableObj = { x: 1, y: 2 };
|
|
19
|
+
* const readonlyObj = castReadonly(mutableObj);
|
|
20
|
+
* // readonlyObj.x = 5; // ❌ TypeScript Error: cannot assign to readonly property
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Protecting function return values
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Prevent callers from mutating internal state
|
|
26
|
+
* class UserService {
|
|
27
|
+
* private users: User[] = [];
|
|
28
|
+
*
|
|
29
|
+
* getUsers(): readonly User[] {
|
|
30
|
+
* return castReadonly(this.users); // Callers can't mutate the array
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* const service = new UserService();
|
|
35
|
+
* const users = service.getUsers();
|
|
36
|
+
* // users.push(newUser); // ❌ TypeScript prevents this
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Creating immutable configurations
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Start with mutable object for initialization
|
|
42
|
+
* const config = {
|
|
43
|
+
* apiUrl: 'https://api.example.com',
|
|
44
|
+
* timeout: 5000,
|
|
45
|
+
* retries: 3
|
|
46
|
+
* };
|
|
47
|
+
*
|
|
48
|
+
* // Validate and process config...
|
|
49
|
+
*
|
|
50
|
+
* // Export as readonly to prevent modifications
|
|
51
|
+
* export const APP_CONFIG = castReadonly(config);
|
|
52
|
+
* // APP_CONFIG.timeout = 10000; // ❌ TypeScript Error
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example Working with array methods
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const numbers: number[] = [1, 2, 3, 4, 5];
|
|
58
|
+
* const readonlyNumbers = castReadonly(numbers);
|
|
59
|
+
*
|
|
60
|
+
* // Read operations still work
|
|
61
|
+
* const doubled = readonlyNumbers.map(n => n * 2); // ✅ Returns new array
|
|
62
|
+
* const sum = readonlyNumbers.reduce((a, b) => a + b, 0); // ✅ Works
|
|
63
|
+
* const first = readonlyNumbers[0]; // ✅ Reading is allowed
|
|
64
|
+
*
|
|
65
|
+
* // Mutations are prevented
|
|
66
|
+
* // readonlyNumbers[0] = 10; // ❌ TypeScript Error
|
|
67
|
+
* // readonlyNumbers.sort(); // ❌ TypeScript Error (sort mutates)
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @see castDeepReadonly - For deeply nested structures
|
|
71
|
+
* @see castMutable - For the opposite operation (use with caution)
|
|
72
|
+
*/
|
|
73
|
+
export declare const castReadonly: <T>(mutable: T) => Readonly<T>;
|
|
74
|
+
/**
|
|
75
|
+
* Casts a mutable type `T` to its `DeepReadonly<T>` equivalent, recursively adding readonly modifiers.
|
|
76
|
+
*
|
|
77
|
+
* This is a safe type assertion that adds immutability constraints at ALL levels of nesting.
|
|
78
|
+
* Provides complete protection against mutations in complex data structures.
|
|
79
|
+
* The runtime value is unchanged - only TypeScript's type checking is enhanced.
|
|
80
|
+
*
|
|
81
|
+
* @template T - The type of the mutable value
|
|
82
|
+
* @param mutable - The mutable value to cast to deeply readonly
|
|
83
|
+
* @returns The same value with readonly modifiers recursively added to all properties
|
|
84
|
+
*
|
|
85
|
+
* @example Basic usage with nested structures
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const mutableNested = {
|
|
88
|
+
* a: { b: [1, 2, 3] },
|
|
89
|
+
* c: { d: { e: 'value' } }
|
|
90
|
+
* };
|
|
91
|
+
*
|
|
92
|
+
* const readonlyNested = castDeepReadonly(mutableNested);
|
|
93
|
+
* // readonlyNested.a.b.push(4); // ❌ Error: readonly at all levels
|
|
94
|
+
* // readonlyNested.c.d.e = 'new'; // ❌ Error: readonly at all levels
|
|
95
|
+
* // readonlyNested.a = {}; // ❌ Error: cannot reassign readonly property
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example Protecting complex state objects
|
|
99
|
+
* ```typescript
|
|
100
|
+
* interface AppState {
|
|
101
|
+
* user: {
|
|
102
|
+
* id: number;
|
|
103
|
+
* profile: {
|
|
104
|
+
* name: string;
|
|
105
|
+
* settings: {
|
|
106
|
+
* theme: string;
|
|
107
|
+
* notifications: boolean[];
|
|
108
|
+
* };
|
|
109
|
+
* };
|
|
110
|
+
* };
|
|
111
|
+
* data: {
|
|
112
|
+
* items: Array<{ id: number; value: string }>;
|
|
113
|
+
* };
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* class StateManager {
|
|
117
|
+
* private state: AppState = initialState;
|
|
118
|
+
*
|
|
119
|
+
* getState(): DeepReadonly<AppState> {
|
|
120
|
+
* return castDeepReadonly(this.state);
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* // Mutations only allowed through specific methods
|
|
124
|
+
* updateTheme(theme: string) {
|
|
125
|
+
* this.state.user.profile.settings.theme = theme;
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Creating immutable API responses
|
|
131
|
+
* ```typescript
|
|
132
|
+
* async function fetchUserData(): Promise<DeepReadonly<UserData>> {
|
|
133
|
+
* const response = await api.get<UserData>('/user');
|
|
134
|
+
*
|
|
135
|
+
* // Process and validate data...
|
|
136
|
+
*
|
|
137
|
+
* // Return as deeply immutable to prevent accidental mutations
|
|
138
|
+
* return castDeepReadonly(response.data);
|
|
139
|
+
* }
|
|
140
|
+
*
|
|
141
|
+
* const userData = await fetchUserData();
|
|
142
|
+
* // userData is fully protected from mutations at any depth
|
|
143
|
+
* // userData.preferences.emails.push('new@email.com'); // ❌ TypeScript Error
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @example Working with Redux or state management
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // Redux reducer example
|
|
149
|
+
* type State = DeepReadonly<AppState>;
|
|
150
|
+
*
|
|
151
|
+
* function reducer(state: State, action: Action): State {
|
|
152
|
+
* switch (action.type) {
|
|
153
|
+
* case 'UPDATE_USER_NAME':
|
|
154
|
+
* // Must create new objects, can't mutate
|
|
155
|
+
* return castDeepReadonly({
|
|
156
|
+
* ...state,
|
|
157
|
+
* user: {
|
|
158
|
+
* ...state.user,
|
|
159
|
+
* profile: {
|
|
160
|
+
* ...state.user.profile,
|
|
161
|
+
* name: action.payload
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
* });
|
|
165
|
+
* default:
|
|
166
|
+
* return state;
|
|
167
|
+
* }
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @example Type inference with generics
|
|
172
|
+
* ```typescript
|
|
173
|
+
* function processData<T>(data: T): DeepReadonly<T> {
|
|
174
|
+
* // Perform processing...
|
|
175
|
+
* console.log('Processing:', data);
|
|
176
|
+
*
|
|
177
|
+
* // Return immutable version
|
|
178
|
+
* return castDeepReadonly(data);
|
|
179
|
+
* }
|
|
180
|
+
*
|
|
181
|
+
* const result = processData({ nested: { value: [1, 2, 3] } });
|
|
182
|
+
* // Type of result is DeepReadonly<{ nested: { value: number[] } }>
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @see castReadonly - For shallow readonly casting
|
|
186
|
+
* @see castDeepMutable - For the opposite operation (use with extreme caution)
|
|
187
|
+
*/
|
|
188
|
+
export declare const castDeepReadonly: <T>(mutable: T) => DeepReadonly<T>;
|
|
189
|
+
//# sourceMappingURL=cast-readonly.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast-readonly.d.mts","sourceRoot":"","sources":["../../src/others/cast-readonly.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AACH,eAAO,MAAM,YAAY,GAAI,CAAC,EAAG,SAAS,CAAC,KAAG,QAAQ,CAAC,CAAC,CAChC,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiHG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAG,SAAS,CAAC,KAAG,YAAY,CAAC,CAAC,CAEpC,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Casts a mutable type `T` to its `Readonly<T>` equivalent.
|
|
3
|
+
*
|
|
4
|
+
* This is a safe type assertion that adds immutability constraints at the type level.
|
|
5
|
+
* The runtime value remains unchanged - only TypeScript's view of it becomes readonly.
|
|
6
|
+
* This helps prevent accidental mutations and makes code intentions clearer.
|
|
7
|
+
*
|
|
8
|
+
* @template T - The type of the mutable value
|
|
9
|
+
* @param mutable - The mutable value to cast to readonly
|
|
10
|
+
* @returns The same value with readonly modifiers added to its type
|
|
11
|
+
*
|
|
12
|
+
* @example Basic usage with arrays and objects
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const mutableArr: number[] = [1, 2, 3];
|
|
15
|
+
* const readonlyArr = castReadonly(mutableArr);
|
|
16
|
+
* // readonlyArr.push(4); // ❌ TypeScript Error: no 'push' on readonly array
|
|
17
|
+
*
|
|
18
|
+
* const mutableObj = { x: 1, y: 2 };
|
|
19
|
+
* const readonlyObj = castReadonly(mutableObj);
|
|
20
|
+
* // readonlyObj.x = 5; // ❌ TypeScript Error: cannot assign to readonly property
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @example Protecting function return values
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Prevent callers from mutating internal state
|
|
26
|
+
* class UserService {
|
|
27
|
+
* private users: User[] = [];
|
|
28
|
+
*
|
|
29
|
+
* getUsers(): readonly User[] {
|
|
30
|
+
* return castReadonly(this.users); // Callers can't mutate the array
|
|
31
|
+
* }
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* const service = new UserService();
|
|
35
|
+
* const users = service.getUsers();
|
|
36
|
+
* // users.push(newUser); // ❌ TypeScript prevents this
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @example Creating immutable configurations
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Start with mutable object for initialization
|
|
42
|
+
* const config = {
|
|
43
|
+
* apiUrl: 'https://api.example.com',
|
|
44
|
+
* timeout: 5000,
|
|
45
|
+
* retries: 3
|
|
46
|
+
* };
|
|
47
|
+
*
|
|
48
|
+
* // Validate and process config...
|
|
49
|
+
*
|
|
50
|
+
* // Export as readonly to prevent modifications
|
|
51
|
+
* export const APP_CONFIG = castReadonly(config);
|
|
52
|
+
* // APP_CONFIG.timeout = 10000; // ❌ TypeScript Error
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* @example Working with array methods
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const numbers: number[] = [1, 2, 3, 4, 5];
|
|
58
|
+
* const readonlyNumbers = castReadonly(numbers);
|
|
59
|
+
*
|
|
60
|
+
* // Read operations still work
|
|
61
|
+
* const doubled = readonlyNumbers.map(n => n * 2); // ✅ Returns new array
|
|
62
|
+
* const sum = readonlyNumbers.reduce((a, b) => a + b, 0); // ✅ Works
|
|
63
|
+
* const first = readonlyNumbers[0]; // ✅ Reading is allowed
|
|
64
|
+
*
|
|
65
|
+
* // Mutations are prevented
|
|
66
|
+
* // readonlyNumbers[0] = 10; // ❌ TypeScript Error
|
|
67
|
+
* // readonlyNumbers.sort(); // ❌ TypeScript Error (sort mutates)
|
|
68
|
+
* ```
|
|
69
|
+
*
|
|
70
|
+
* @see castDeepReadonly - For deeply nested structures
|
|
71
|
+
* @see castMutable - For the opposite operation (use with caution)
|
|
72
|
+
*/
|
|
73
|
+
const castReadonly = (mutable) => mutable;
|
|
74
|
+
/**
|
|
75
|
+
* Casts a mutable type `T` to its `DeepReadonly<T>` equivalent, recursively adding readonly modifiers.
|
|
76
|
+
*
|
|
77
|
+
* This is a safe type assertion that adds immutability constraints at ALL levels of nesting.
|
|
78
|
+
* Provides complete protection against mutations in complex data structures.
|
|
79
|
+
* The runtime value is unchanged - only TypeScript's type checking is enhanced.
|
|
80
|
+
*
|
|
81
|
+
* @template T - The type of the mutable value
|
|
82
|
+
* @param mutable - The mutable value to cast to deeply readonly
|
|
83
|
+
* @returns The same value with readonly modifiers recursively added to all properties
|
|
84
|
+
*
|
|
85
|
+
* @example Basic usage with nested structures
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const mutableNested = {
|
|
88
|
+
* a: { b: [1, 2, 3] },
|
|
89
|
+
* c: { d: { e: 'value' } }
|
|
90
|
+
* };
|
|
91
|
+
*
|
|
92
|
+
* const readonlyNested = castDeepReadonly(mutableNested);
|
|
93
|
+
* // readonlyNested.a.b.push(4); // ❌ Error: readonly at all levels
|
|
94
|
+
* // readonlyNested.c.d.e = 'new'; // ❌ Error: readonly at all levels
|
|
95
|
+
* // readonlyNested.a = {}; // ❌ Error: cannot reassign readonly property
|
|
96
|
+
* ```
|
|
97
|
+
*
|
|
98
|
+
* @example Protecting complex state objects
|
|
99
|
+
* ```typescript
|
|
100
|
+
* interface AppState {
|
|
101
|
+
* user: {
|
|
102
|
+
* id: number;
|
|
103
|
+
* profile: {
|
|
104
|
+
* name: string;
|
|
105
|
+
* settings: {
|
|
106
|
+
* theme: string;
|
|
107
|
+
* notifications: boolean[];
|
|
108
|
+
* };
|
|
109
|
+
* };
|
|
110
|
+
* };
|
|
111
|
+
* data: {
|
|
112
|
+
* items: Array<{ id: number; value: string }>;
|
|
113
|
+
* };
|
|
114
|
+
* }
|
|
115
|
+
*
|
|
116
|
+
* class StateManager {
|
|
117
|
+
* private state: AppState = initialState;
|
|
118
|
+
*
|
|
119
|
+
* getState(): DeepReadonly<AppState> {
|
|
120
|
+
* return castDeepReadonly(this.state);
|
|
121
|
+
* }
|
|
122
|
+
*
|
|
123
|
+
* // Mutations only allowed through specific methods
|
|
124
|
+
* updateTheme(theme: string) {
|
|
125
|
+
* this.state.user.profile.settings.theme = theme;
|
|
126
|
+
* }
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* @example Creating immutable API responses
|
|
131
|
+
* ```typescript
|
|
132
|
+
* async function fetchUserData(): Promise<DeepReadonly<UserData>> {
|
|
133
|
+
* const response = await api.get<UserData>('/user');
|
|
134
|
+
*
|
|
135
|
+
* // Process and validate data...
|
|
136
|
+
*
|
|
137
|
+
* // Return as deeply immutable to prevent accidental mutations
|
|
138
|
+
* return castDeepReadonly(response.data);
|
|
139
|
+
* }
|
|
140
|
+
*
|
|
141
|
+
* const userData = await fetchUserData();
|
|
142
|
+
* // userData is fully protected from mutations at any depth
|
|
143
|
+
* // userData.preferences.emails.push('new@email.com'); // ❌ TypeScript Error
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @example Working with Redux or state management
|
|
147
|
+
* ```typescript
|
|
148
|
+
* // Redux reducer example
|
|
149
|
+
* type State = DeepReadonly<AppState>;
|
|
150
|
+
*
|
|
151
|
+
* function reducer(state: State, action: Action): State {
|
|
152
|
+
* switch (action.type) {
|
|
153
|
+
* case 'UPDATE_USER_NAME':
|
|
154
|
+
* // Must create new objects, can't mutate
|
|
155
|
+
* return castDeepReadonly({
|
|
156
|
+
* ...state,
|
|
157
|
+
* user: {
|
|
158
|
+
* ...state.user,
|
|
159
|
+
* profile: {
|
|
160
|
+
* ...state.user.profile,
|
|
161
|
+
* name: action.payload
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
* });
|
|
165
|
+
* default:
|
|
166
|
+
* return state;
|
|
167
|
+
* }
|
|
168
|
+
* }
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @example Type inference with generics
|
|
172
|
+
* ```typescript
|
|
173
|
+
* function processData<T>(data: T): DeepReadonly<T> {
|
|
174
|
+
* // Perform processing...
|
|
175
|
+
* console.log('Processing:', data);
|
|
176
|
+
*
|
|
177
|
+
* // Return immutable version
|
|
178
|
+
* return castDeepReadonly(data);
|
|
179
|
+
* }
|
|
180
|
+
*
|
|
181
|
+
* const result = processData({ nested: { value: [1, 2, 3] } });
|
|
182
|
+
* // Type of result is DeepReadonly<{ nested: { value: number[] } }>
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
* @see castReadonly - For shallow readonly casting
|
|
186
|
+
* @see castDeepMutable - For the opposite operation (use with extreme caution)
|
|
187
|
+
*/
|
|
188
|
+
const castDeepReadonly = (mutable) =>
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
190
|
+
mutable;
|
|
191
|
+
|
|
192
|
+
export { castDeepReadonly, castReadonly };
|
|
193
|
+
//# sourceMappingURL=cast-readonly.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast-readonly.mjs","sources":["../../src/others/cast-readonly.mts"],"sourcesContent":[null],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuEG;AACI,MAAM,YAAY,GAAG,CAAK,OAAU,KACzC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiHG;AACI,MAAM,gBAAgB,GAAG,CAAK,OAAU;AAC7C;AACA;;;;"}
|