tuple-ts 0.2.1 → 0.2.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 +155 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,3 +1,157 @@
|
|
|
1
1
|
# TupleTS
|
|
2
2
|
|
|
3
|
-
TupleTS is an implementation of interned tuples and records intended to be memory-safe, performant, and
|
|
3
|
+
TupleTS is an implementation of interned tuples and records intended to be memory-safe, performant, and tiny.
|
|
4
|
+
|
|
5
|
+
- **Referential Equality (`===`)**: Identical tuples and records return the same instance in memory.
|
|
6
|
+
- **Composite Keys for `Set` & `Map`**: Use tuples or records as keys without custom serialization or hashing workarounds.
|
|
7
|
+
- **Memory-Safe**: Uses `WeakRef` and `FinalizationRegistry` to automatically purge entries upon garbage collection.
|
|
8
|
+
- **Deeply Nestable & Immutable**: Tuples can contain other tuples; all created tuples and records are `Object.freeze`'d.
|
|
9
|
+
- **TypeScript First**: Full type inference and type-safe factories.
|
|
10
|
+
- **Zero Dependencies**: Lightweight implementation with fast MurmurHash3-based hashing.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install tuple-ts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Usage Examples
|
|
23
|
+
|
|
24
|
+
### 1. Basic Tuples & Referential Equality
|
|
25
|
+
|
|
26
|
+
Tuples with the same elements evaluate to the exact same object reference (`===`):
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { tuple } from "tuple-ts";
|
|
30
|
+
|
|
31
|
+
const a = tuple(1, "hello", true);
|
|
32
|
+
const b = tuple(1, "hello", true);
|
|
33
|
+
|
|
34
|
+
console.log(a === b); // true
|
|
35
|
+
|
|
36
|
+
// Tuples are standard frozen arrays:
|
|
37
|
+
console.log(a[0]); // 1
|
|
38
|
+
console.log(a.length); // 3
|
|
39
|
+
console.log([...a]); // [1, "hello", true]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Nested Tuples
|
|
43
|
+
|
|
44
|
+
Tuples can be nested arbitrarily and maintain referential equality across structures:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { tuple } from "tuple-ts";
|
|
48
|
+
|
|
49
|
+
const t1 = tuple("matrix", tuple(1, 0), tuple(0, 1));
|
|
50
|
+
const t2 = tuple("matrix", tuple(1, 0), tuple(0, 1));
|
|
51
|
+
const t3 = tuple("matrix", tuple(1, 1), tuple(0, 1));
|
|
52
|
+
|
|
53
|
+
console.log(t1 === t2); // true
|
|
54
|
+
console.log(t1 === t3); // false
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Composite Keys in `Map` and `Set`
|
|
58
|
+
|
|
59
|
+
In standard JavaScript, arrays and objects compare by reference, preventing them from being used as composite keys in `Map` or values in `Set`. With `tuple`, structural equality makes composite keys work seamlessly:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { tuple } from "tuple-ts";
|
|
63
|
+
|
|
64
|
+
// Set: Deduplicating coordinate pairs
|
|
65
|
+
const visited = new Set();
|
|
66
|
+
visited.add(tuple(10, 20));
|
|
67
|
+
|
|
68
|
+
console.log(visited.has(tuple(10, 20))); // true!
|
|
69
|
+
console.log(visited.has(tuple(10, 21))); // false
|
|
70
|
+
|
|
71
|
+
// Map: Multi-argument function memoization / 2D grid
|
|
72
|
+
const grid = new Map();
|
|
73
|
+
grid.set(tuple(0, 0), "Origin");
|
|
74
|
+
grid.set(tuple(3, 4), "Target");
|
|
75
|
+
|
|
76
|
+
console.log(grid.get(tuple(0, 0))); // "Origin"
|
|
77
|
+
console.log(grid.get(tuple(3, 4))); // "Target"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Custom Typed Tuples (`tupleType`)
|
|
81
|
+
|
|
82
|
+
You can create isolated, strictly-typed tuple factories with their own independent intern caches:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { tupleType } from "tuple-ts";
|
|
86
|
+
|
|
87
|
+
type Point2D = [x: number, y: number];
|
|
88
|
+
const Point = tupleType<Point2D>();
|
|
89
|
+
|
|
90
|
+
const p1 = Point(12, 34);
|
|
91
|
+
const p2 = Point(12, 34);
|
|
92
|
+
|
|
93
|
+
console.log(p1 === p2); // true
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 5. Interned Records (`record`)
|
|
97
|
+
|
|
98
|
+
Just like tuples, `record` interns shallow objects. Property key insertion order does not affect equality:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { record } from "tuple-ts";
|
|
102
|
+
|
|
103
|
+
const userA = record({ id: 1, role: "admin" });
|
|
104
|
+
const userB = record({ role: "admin", id: 1 }); // different key order
|
|
105
|
+
|
|
106
|
+
console.log(userA === userB); // true
|
|
107
|
+
console.log(userA.role); // "admin"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### 6. Keyed Records (`recordType().withKeys(...)`)
|
|
111
|
+
|
|
112
|
+
Define records interned and deduplicated on a specific subset of primary keys:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { recordType } from "tuple-ts";
|
|
116
|
+
|
|
117
|
+
interface UserProfile {
|
|
118
|
+
id: string;
|
|
119
|
+
name: string;
|
|
120
|
+
updatedAt: number;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Only 'id' determines record identity
|
|
124
|
+
const UserById = recordType<UserProfile>().withKeys("id");
|
|
125
|
+
|
|
126
|
+
const session1 = UserById({ id: "usr_1", name: "Alice", updatedAt: 1000 });
|
|
127
|
+
const session2 = UserById({ id: "usr_1", name: "Alice B.", updatedAt: 2000 });
|
|
128
|
+
|
|
129
|
+
console.log(session1 === session2); // true
|
|
130
|
+
console.log(session1); // { id: "usr_1" }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 7. Memory Safety & Garbage Collection
|
|
134
|
+
|
|
135
|
+
TupleTS uses `WeakRef` and `FinalizationRegistry` under the hood. When a tuple or record is no longer referenced anywhere in your program, the JavaScript runtime garbage-collects it, and its entry in the intern cache is automatically pruned.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
// No need to manually clear caches or worry about memory leaks!
|
|
139
|
+
{
|
|
140
|
+
const temp = tuple("transient", 123);
|
|
141
|
+
// once 'temp' leaves scope and GC runs, the intern cache entry is freed.
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Equality Semantics
|
|
148
|
+
|
|
149
|
+
TupleTS compares primitives via `Object.is`:
|
|
150
|
+
- `NaN` is equivalent to `NaN` (`tuple(NaN) === tuple(NaN)`).
|
|
151
|
+
- `+0` and `-0` are treated as distinct values (`tuple(+0) !== tuple(-0)`).
|
|
152
|
+
- Object elements are interned by object identity:
|
|
153
|
+
```ts
|
|
154
|
+
const shared = { value: 1 };
|
|
155
|
+
tuple(shared) === tuple(shared); // true
|
|
156
|
+
tuple({}) === tuple({}); // false (different object references)
|
|
157
|
+
```
|