mutts 1.0.0
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 +150 -0
- package/dist/chunks/decorator-BXsign4Z.js +176 -0
- package/dist/chunks/decorator-BXsign4Z.js.map +1 -0
- package/dist/chunks/decorator-CPbZNnsX.esm.js +168 -0
- package/dist/chunks/decorator-CPbZNnsX.esm.js.map +1 -0
- package/dist/decorator.d.ts +50 -0
- package/dist/decorator.esm.js +2 -0
- package/dist/decorator.esm.js.map +1 -0
- package/dist/decorator.js +11 -0
- package/dist/decorator.js.map +1 -0
- package/dist/destroyable.d.ts +48 -0
- package/dist/destroyable.esm.js +91 -0
- package/dist/destroyable.esm.js.map +1 -0
- package/dist/destroyable.js +98 -0
- package/dist/destroyable.js.map +1 -0
- package/dist/eventful.d.ts +11 -0
- package/dist/eventful.esm.js +88 -0
- package/dist/eventful.esm.js.map +1 -0
- package/dist/eventful.js +90 -0
- package/dist/eventful.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.esm.js +7 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/indexable.d.ts +31 -0
- package/dist/indexable.esm.js +85 -0
- package/dist/indexable.esm.js.map +1 -0
- package/dist/indexable.js +89 -0
- package/dist/indexable.js.map +1 -0
- package/dist/mutts.umd.js +2 -0
- package/dist/mutts.umd.js.map +1 -0
- package/dist/mutts.umd.min.js +2 -0
- package/dist/mutts.umd.min.js.map +1 -0
- package/dist/promiseChain.d.ts +11 -0
- package/dist/promiseChain.esm.js +72 -0
- package/dist/promiseChain.esm.js.map +1 -0
- package/dist/promiseChain.js +74 -0
- package/dist/promiseChain.js.map +1 -0
- package/dist/reactive.d.ts +114 -0
- package/dist/reactive.esm.js +1455 -0
- package/dist/reactive.esm.js.map +1 -0
- package/dist/reactive.js +1472 -0
- package/dist/reactive.js.map +1 -0
- package/dist/std-decorators.d.ts +17 -0
- package/dist/std-decorators.esm.js +161 -0
- package/dist/std-decorators.esm.js.map +1 -0
- package/dist/std-decorators.js +169 -0
- package/dist/std-decorators.js.map +1 -0
- package/docs/decorator.md +300 -0
- package/docs/destroyable.md +294 -0
- package/docs/events.md +225 -0
- package/docs/indexable.md +561 -0
- package/docs/promiseChain.md +218 -0
- package/docs/reactive.md +2072 -0
- package/docs/std-decorators.md +558 -0
- package/package.json +132 -0
- package/src/decorator.test.ts +495 -0
- package/src/decorator.ts +205 -0
- package/src/destroyable.test.ts +155 -0
- package/src/destroyable.ts +158 -0
- package/src/eventful.test.ts +380 -0
- package/src/eventful.ts +69 -0
- package/src/index.ts +7 -0
- package/src/indexable.test.ts +388 -0
- package/src/indexable.ts +124 -0
- package/src/promiseChain.test.ts +201 -0
- package/src/promiseChain.ts +99 -0
- package/src/reactive/array.test.ts +923 -0
- package/src/reactive/array.ts +352 -0
- package/src/reactive/core.test.ts +1663 -0
- package/src/reactive/core.ts +866 -0
- package/src/reactive/index.ts +28 -0
- package/src/reactive/interface.test.ts +1477 -0
- package/src/reactive/interface.ts +231 -0
- package/src/reactive/map.test.ts +866 -0
- package/src/reactive/map.ts +162 -0
- package/src/reactive/set.test.ts +289 -0
- package/src/reactive/set.ts +142 -0
- package/src/std-decorators.test.ts +679 -0
- package/src/std-decorators.ts +182 -0
- package/src/utils.ts +52 -0
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
# Indexable Documentation
|
|
2
|
+
|
|
3
|
+
## 1. Overview
|
|
4
|
+
|
|
5
|
+
### Introduction
|
|
6
|
+
|
|
7
|
+
The `Indexable` utility is a powerful TypeScript/JavaScript function that enables classes to have numeric index access similar to arrays, while maintaining full control over how values are retrieved and stored. It uses JavaScript's Proxy API to intercept property access and provide custom indexing behavior.
|
|
8
|
+
|
|
9
|
+
### What is Indexable?
|
|
10
|
+
|
|
11
|
+
`Indexable` is a factory function that creates classes with numeric index access capabilities. It allows you to:
|
|
12
|
+
- Access object properties using numeric indices (e.g., `obj[0]`, `obj[1]`)
|
|
13
|
+
- Customize how values are retrieved and stored
|
|
14
|
+
- Extend existing classes with index access
|
|
15
|
+
- Create read-only or read-write indexable objects
|
|
16
|
+
- Maintain type safety with TypeScript
|
|
17
|
+
|
|
18
|
+
### Key Concepts
|
|
19
|
+
|
|
20
|
+
- **Index Access**: Numeric property access like arrays (`obj[0]`)
|
|
21
|
+
- **Proxy Interception**: Uses JavaScript Proxy to intercept property access
|
|
22
|
+
- **Accessor Functions**: Custom functions that define how to get/set values
|
|
23
|
+
- **Symbol-Based Methods**: Uses `getAt` and `setAt` symbols for custom access logic
|
|
24
|
+
- **Type Safety**: Full TypeScript support with generic types
|
|
25
|
+
|
|
26
|
+
### Use Cases
|
|
27
|
+
|
|
28
|
+
- Custom collection classes
|
|
29
|
+
- Data structures with numeric indexing
|
|
30
|
+
- Wrapper classes for external data sources
|
|
31
|
+
- Immutable data structures
|
|
32
|
+
- Performance-optimized access patterns
|
|
33
|
+
|
|
34
|
+
## 2. API Reference
|
|
35
|
+
|
|
36
|
+
### Core Symbols
|
|
37
|
+
|
|
38
|
+
#### getAt
|
|
39
|
+
```typescript
|
|
40
|
+
export const getAt = Symbol('getAt')
|
|
41
|
+
```
|
|
42
|
+
A symbol used to define custom getter logic for numeric index access. Classes can implement this method to control how values are retrieved.
|
|
43
|
+
|
|
44
|
+
#### setAt
|
|
45
|
+
```typescript
|
|
46
|
+
export const setAt = Symbol('setAt')
|
|
47
|
+
```
|
|
48
|
+
A symbol used to define custom setter logic for numeric index access. Classes can implement this method to control how values are stored.
|
|
49
|
+
|
|
50
|
+
### Main Function
|
|
51
|
+
|
|
52
|
+
#### Indexable()
|
|
53
|
+
```typescript
|
|
54
|
+
export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
|
|
55
|
+
base: Base,
|
|
56
|
+
accessor: Accessor<InstanceType<Base>, Items>
|
|
57
|
+
): new (...args: ConstructorParameters<Base>) => InstanceType<Base> & { [x: number]: Items }
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The main factory function that creates indexable classes. It has multiple overloads to support different use cases.
|
|
61
|
+
|
|
62
|
+
### Interfaces
|
|
63
|
+
|
|
64
|
+
#### IndexingAt
|
|
65
|
+
```typescript
|
|
66
|
+
interface IndexingAt<Items = any> {
|
|
67
|
+
[getAt](index: number): Items
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
Interface for classes that implement custom getter logic using the `getAt` symbol.
|
|
71
|
+
|
|
72
|
+
#### Accessor
|
|
73
|
+
```typescript
|
|
74
|
+
interface Accessor<T, Items> {
|
|
75
|
+
get(this: T, index: number): Items
|
|
76
|
+
set?(this: T, index: number, value: Items): void
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
Interface defining how to access and optionally modify values at numeric indices.
|
|
80
|
+
|
|
81
|
+
#### AbstractGetAt
|
|
82
|
+
```typescript
|
|
83
|
+
abstract class AbstractGetAt<Items = any> {
|
|
84
|
+
abstract [getAt](index: number): Items
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
Abstract base class for creating indexable classes with custom getter logic.
|
|
88
|
+
|
|
89
|
+
### Type Utilities
|
|
90
|
+
|
|
91
|
+
#### AtReturnType
|
|
92
|
+
```typescript
|
|
93
|
+
type AtReturnType<T> = T extends { [getAt](index: number): infer R } ? R : never
|
|
94
|
+
```
|
|
95
|
+
Utility type that extracts the return type from a class's `getAt` method.
|
|
96
|
+
|
|
97
|
+
## 3. Function Overloads
|
|
98
|
+
|
|
99
|
+
### Overload 1: Base Class with Accessor
|
|
100
|
+
```typescript
|
|
101
|
+
Indexable<Items, Base>(base: Base, accessor: Accessor<InstanceType<Base>, Items>)
|
|
102
|
+
```
|
|
103
|
+
Creates an indexable class that extends an existing base class with custom accessor functions.
|
|
104
|
+
|
|
105
|
+
**Example:**
|
|
106
|
+
```typescript
|
|
107
|
+
class MyClass {
|
|
108
|
+
constructor(public data: number[]) {}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const IndexableMyClass = Indexable(MyClass, {
|
|
112
|
+
get(this: MyClass, index: number) {
|
|
113
|
+
return this.data[index] * 2; // Custom access logic
|
|
114
|
+
},
|
|
115
|
+
set(this: MyClass, index: number, value: number) {
|
|
116
|
+
this.data[index] = value / 2; // Custom storage logic
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const instance = new IndexableMyClass([1, 2, 3]);
|
|
121
|
+
console.log(instance[0]); // 2 (1 * 2)
|
|
122
|
+
instance[1] = 8; // Stores 4 (8 / 2)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Overload 2: Accessor Only
|
|
126
|
+
```typescript
|
|
127
|
+
Indexable<Items>(accessor: Accessor<any, Items>)
|
|
128
|
+
```
|
|
129
|
+
Creates an indexable class with custom accessor functions but no base class.
|
|
130
|
+
|
|
131
|
+
**Example:**
|
|
132
|
+
```typescript
|
|
133
|
+
const IndexableClass = Indexable({
|
|
134
|
+
get(this: any, index: number) {
|
|
135
|
+
return `Item ${index}`;
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const instance = new IndexableClass();
|
|
140
|
+
console.log(instance[0]); // "Item 0"
|
|
141
|
+
console.log(instance[5]); // "Item 5"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Overload 3: Base Class with getAt Method
|
|
145
|
+
```typescript
|
|
146
|
+
Indexable<Base extends new (...args: any[]) => IndexingAt>(base: Base)
|
|
147
|
+
```
|
|
148
|
+
Creates an indexable class from a base class that already implements the `getAt` method.
|
|
149
|
+
|
|
150
|
+
**Example:**
|
|
151
|
+
```typescript
|
|
152
|
+
class DataStore {
|
|
153
|
+
constructor(private items: string[]) {}
|
|
154
|
+
|
|
155
|
+
[getAt](index: number): string {
|
|
156
|
+
return this.items[index] || 'default';
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const IndexableDataStore = Indexable(DataStore);
|
|
161
|
+
const store = new IndexableDataStore(['a', 'b', 'c']);
|
|
162
|
+
console.log(store[0]); // "a"
|
|
163
|
+
console.log(store[10]); // "default"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Overload 4: Abstract Class
|
|
167
|
+
```typescript
|
|
168
|
+
Indexable<Items>()
|
|
169
|
+
```
|
|
170
|
+
Creates an abstract indexable class that must be extended with a `getAt` implementation.
|
|
171
|
+
|
|
172
|
+
**Example:**
|
|
173
|
+
```typescript
|
|
174
|
+
const AbstractIndexable = Indexable<string>();
|
|
175
|
+
|
|
176
|
+
class StringArray extends AbstractIndexable {
|
|
177
|
+
constructor(private items: string[]) {
|
|
178
|
+
super();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[getAt](index: number): string {
|
|
182
|
+
return this.items[index] || '';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const array = new StringArray(['hello', 'world']);
|
|
187
|
+
console.log(array[0]); // "hello"
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 4. Implementation Details
|
|
191
|
+
|
|
192
|
+
### Proxy-Based Architecture
|
|
193
|
+
|
|
194
|
+
The Indexable utility uses JavaScript's Proxy API to intercept property access. When you access a numeric property (e.g., `obj[0]`), the proxy:
|
|
195
|
+
|
|
196
|
+
1. Checks if the property exists on the target object
|
|
197
|
+
2. If it's a numeric property, calls the appropriate accessor function
|
|
198
|
+
3. If it's a regular property, delegates to the original object
|
|
199
|
+
4. Handles property setting with similar logic
|
|
200
|
+
|
|
201
|
+
### Prototype Chain Management
|
|
202
|
+
|
|
203
|
+
The utility carefully manages the prototype chain to ensure:
|
|
204
|
+
- Methods from the base class are accessible
|
|
205
|
+
- The proxy intercepts numeric property access
|
|
206
|
+
- Regular property access works normally
|
|
207
|
+
- Inheritance works correctly
|
|
208
|
+
|
|
209
|
+
### Property Access Interception
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// Simplified version of the proxy logic
|
|
213
|
+
new Proxy(base.prototype, {
|
|
214
|
+
get(target, prop, receiver) {
|
|
215
|
+
if (typeof prop === 'string') {
|
|
216
|
+
const numProp = Number(prop);
|
|
217
|
+
if (!Number.isNaN(numProp)) {
|
|
218
|
+
return accessor.get.call(receiver, numProp);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return target[prop];
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Error Handling
|
|
227
|
+
|
|
228
|
+
The utility provides clear error messages for common issues:
|
|
229
|
+
- Missing `getAt` method: "Indexable class must have an [getAt] method"
|
|
230
|
+
- Read-only access: "Indexable class has read-only numeric index access"
|
|
231
|
+
|
|
232
|
+
## 5. Usage Examples
|
|
233
|
+
|
|
234
|
+
### Basic Usage
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// Create a simple indexable class
|
|
238
|
+
const SimpleIndexable = Indexable({
|
|
239
|
+
get(this: any, index: number) {
|
|
240
|
+
return `Value at index ${index}`;
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const obj = new SimpleIndexable();
|
|
245
|
+
console.log(obj[0]); // "Value at index 0"
|
|
246
|
+
console.log(obj[42]); // "Value at index 42"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Custom Accessor Functions
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Create an indexable class with custom logic
|
|
253
|
+
class DataWrapper {
|
|
254
|
+
constructor(private data: any[]) {}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const IndexableWrapper = Indexable(DataWrapper, {
|
|
258
|
+
get(this: DataWrapper, index: number) {
|
|
259
|
+
const value = this.data[index];
|
|
260
|
+
return value ? value.toUpperCase() : 'NOT_FOUND';
|
|
261
|
+
},
|
|
262
|
+
set(this: DataWrapper, index: number, value: any) {
|
|
263
|
+
this.data[index] = value.toLowerCase();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const wrapper = new IndexableWrapper(['hello', 'world']);
|
|
268
|
+
console.log(wrapper[0]); // "HELLO"
|
|
269
|
+
wrapper[1] = 'UNIVERSE';
|
|
270
|
+
console.log(wrapper[1]); // "universe"
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Extending Existing Classes
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// Extend a built-in class
|
|
277
|
+
class CustomArray extends Array {
|
|
278
|
+
constructor(...items: number[]) {
|
|
279
|
+
super(...items);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const IndexableCustomArray = Indexable(CustomArray, {
|
|
284
|
+
get(this: CustomArray, index: number) {
|
|
285
|
+
const value = super[index];
|
|
286
|
+
return value ? value * 2 : 0;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const arr = new IndexableCustomArray(1, 2, 3);
|
|
291
|
+
console.log(arr[0]); // 2 (1 * 2)
|
|
292
|
+
console.log(arr[1]); // 4 (2 * 2)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Read-Only Indexable Classes
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Create a read-only indexable class
|
|
299
|
+
const ReadOnlyIndexable = Indexable({
|
|
300
|
+
get(this: any, index: number) {
|
|
301
|
+
return `Read-only value at ${index}`;
|
|
302
|
+
}
|
|
303
|
+
// No set method = read-only
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const readOnly = new ReadOnlyIndexable();
|
|
307
|
+
console.log(readOnly[0]); // "Read-only value at 0"
|
|
308
|
+
// readOnly[0] = "new value"; // Error: read-only access
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Abstract Indexable Classes
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// Create an abstract indexable class
|
|
315
|
+
const AbstractStringIndexable = Indexable<string>();
|
|
316
|
+
|
|
317
|
+
class StringCollection extends AbstractStringIndexable {
|
|
318
|
+
private items: string[] = [];
|
|
319
|
+
|
|
320
|
+
[getAt](index: number): string {
|
|
321
|
+
return this.items[index] || '';
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
add(item: string) {
|
|
325
|
+
this.items.push(item);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const collection = new StringCollection();
|
|
330
|
+
collection.add('first');
|
|
331
|
+
collection.add('second');
|
|
332
|
+
console.log(collection[0]); // "first"
|
|
333
|
+
console.log(collection[1]); // "second"
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## 6. Advanced Patterns
|
|
337
|
+
|
|
338
|
+
### Combining with Other Decorators
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// Combine with class decorators
|
|
342
|
+
function Logged(target: any) {
|
|
343
|
+
return class extends target {
|
|
344
|
+
constructor(...args: any[]) {
|
|
345
|
+
console.log(`Creating ${target.name}`);
|
|
346
|
+
super(...args);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const LoggedIndexable = Logged(Indexable({
|
|
352
|
+
get(this: any, index: number) {
|
|
353
|
+
return `Item ${index}`;
|
|
354
|
+
}
|
|
355
|
+
}));
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Performance Considerations
|
|
359
|
+
|
|
360
|
+
- **Proxy Overhead**: Each property access goes through the proxy, adding minimal overhead
|
|
361
|
+
- **Memory Usage**: Proxy objects use slightly more memory than regular objects
|
|
362
|
+
- **Caching**: Consider caching frequently accessed values if performance is critical
|
|
363
|
+
- **Batch Operations**: For bulk operations, access the underlying data directly
|
|
364
|
+
|
|
365
|
+
### Memory Management
|
|
366
|
+
|
|
367
|
+
- **Weak References**: Consider using WeakMap/WeakSet for storing references
|
|
368
|
+
- **Cleanup**: Implement cleanup methods if your accessor functions create closures
|
|
369
|
+
- **Circular References**: Be careful to avoid circular references in accessor functions
|
|
370
|
+
|
|
371
|
+
## 7. Troubleshooting
|
|
372
|
+
|
|
373
|
+
### Common Issues
|
|
374
|
+
|
|
375
|
+
**TypeScript Errors:**
|
|
376
|
+
```typescript
|
|
377
|
+
// Error: Property '0' does not exist on type 'MyClass'
|
|
378
|
+
// Solution: Ensure the class is properly typed with Indexable
|
|
379
|
+
const MyIndexableClass = Indexable(MyClass, accessor);
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Runtime Errors:**
|
|
383
|
+
```typescript
|
|
384
|
+
// Error: Indexable class must have an [getAt] method
|
|
385
|
+
// Solution: Implement the getAt method or provide an accessor function
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
**Property Access Issues:**
|
|
389
|
+
```typescript
|
|
390
|
+
// Error: Cannot set property '0' of read-only object
|
|
391
|
+
// Solution: Implement the setAt method or provide a set accessor
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Debugging Tips
|
|
395
|
+
|
|
396
|
+
1. **Check Accessor Functions**: Ensure your accessor functions are properly defined
|
|
397
|
+
2. **Verify Base Class**: Make sure the base class is compatible with Indexable
|
|
398
|
+
3. **Type Checking**: Use TypeScript's strict mode to catch type errors early
|
|
399
|
+
4. **Console Logging**: Add logging to accessor functions to debug access patterns
|
|
400
|
+
|
|
401
|
+
### Performance Issues
|
|
402
|
+
|
|
403
|
+
- **Profile Property Access**: Use browser dev tools to profile property access
|
|
404
|
+
- **Optimize Accessors**: Keep accessor functions lightweight
|
|
405
|
+
- **Consider Alternatives**: For high-performance scenarios, consider direct property access
|
|
406
|
+
|
|
407
|
+
## 8. Best Practices
|
|
408
|
+
|
|
409
|
+
### When to Use Indexable
|
|
410
|
+
|
|
411
|
+
**Use Indexable when:**
|
|
412
|
+
- You need numeric index access on custom classes
|
|
413
|
+
- You want to customize how values are retrieved/stored
|
|
414
|
+
- You're building data structure libraries
|
|
415
|
+
- You need type-safe index access
|
|
416
|
+
|
|
417
|
+
**Consider alternatives when:**
|
|
418
|
+
- Performance is critical and proxy overhead is unacceptable
|
|
419
|
+
- You only need simple array-like behavior (use Array directly)
|
|
420
|
+
- You're working with existing code that doesn't support proxies
|
|
421
|
+
|
|
422
|
+
### Design Patterns
|
|
423
|
+
|
|
424
|
+
1. **Accessor Pattern**: Separate access logic from data storage
|
|
425
|
+
2. **Proxy Pattern**: Use proxies for cross-cutting concerns
|
|
426
|
+
3. **Factory Pattern**: Create indexable classes dynamically
|
|
427
|
+
4. **Template Method**: Define abstract behavior with concrete implementations
|
|
428
|
+
|
|
429
|
+
### Testing Strategies
|
|
430
|
+
|
|
431
|
+
```typescript
|
|
432
|
+
// Test index access
|
|
433
|
+
describe('IndexableClass', () => {
|
|
434
|
+
it('should access values by index', () => {
|
|
435
|
+
const instance = new IndexableClass();
|
|
436
|
+
expect(instance[0]).toBe('expected value');
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('should handle out-of-bounds access', () => {
|
|
440
|
+
const instance = new IndexableClass();
|
|
441
|
+
expect(instance[999]).toBe('default value');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should set values by index', () => {
|
|
445
|
+
const instance = new IndexableClass();
|
|
446
|
+
instance[0] = 'new value';
|
|
447
|
+
expect(instance[0]).toBe('new value');
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 9. Migration Guide
|
|
453
|
+
|
|
454
|
+
### From Manual Index Access
|
|
455
|
+
|
|
456
|
+
**Before:**
|
|
457
|
+
```typescript
|
|
458
|
+
class MyClass {
|
|
459
|
+
getValue(index: number) {
|
|
460
|
+
return this.data[index];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
setValue(index: number, value: any) {
|
|
464
|
+
this.data[index] = value;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const obj = new MyClass();
|
|
469
|
+
const value = obj.getValue(0);
|
|
470
|
+
obj.setValue(1, 'new value');
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**After:**
|
|
474
|
+
```typescript
|
|
475
|
+
const IndexableMyClass = Indexable(MyClass, {
|
|
476
|
+
get(this: MyClass, index: number) {
|
|
477
|
+
return this.data[index];
|
|
478
|
+
},
|
|
479
|
+
set(this: MyClass, index: number, value: any) {
|
|
480
|
+
this.data[index] = value;
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
const obj = new IndexableMyClass();
|
|
485
|
+
const value = obj[0];
|
|
486
|
+
obj[1] = 'new value';
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### From Array-Like Classes
|
|
490
|
+
|
|
491
|
+
**Before:**
|
|
492
|
+
```typescript
|
|
493
|
+
class CustomArray {
|
|
494
|
+
private items: any[] = [];
|
|
495
|
+
|
|
496
|
+
get(index: number) {
|
|
497
|
+
return this.items[index];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
set(index: number, value: any) {
|
|
501
|
+
this.items[index] = value;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
**After:**
|
|
507
|
+
```typescript
|
|
508
|
+
const IndexableCustomArray = Indexable(CustomArray, {
|
|
509
|
+
get(this: CustomArray, index: number) {
|
|
510
|
+
return this.items[index];
|
|
511
|
+
},
|
|
512
|
+
set(this: CustomArray, index: number, value: any) {
|
|
513
|
+
this.items[index] = value;
|
|
514
|
+
}
|
|
515
|
+
});
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Breaking Changes
|
|
519
|
+
|
|
520
|
+
- **Proxy Support**: Requires ES6+ environments with Proxy support
|
|
521
|
+
- **Type Safety**: May require TypeScript configuration updates
|
|
522
|
+
- **Performance**: Introduces proxy overhead for property access
|
|
523
|
+
|
|
524
|
+
## 10. Appendix
|
|
525
|
+
|
|
526
|
+
### TypeScript Configuration
|
|
527
|
+
|
|
528
|
+
```json
|
|
529
|
+
{
|
|
530
|
+
"compilerOptions": {
|
|
531
|
+
"target": "ES6",
|
|
532
|
+
"lib": ["ES6", "DOM"],
|
|
533
|
+
"strict": true,
|
|
534
|
+
"experimentalDecorators": true
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### Browser Compatibility
|
|
540
|
+
|
|
541
|
+
- **ES6+**: Full support in modern browsers
|
|
542
|
+
- **IE11**: No support (no Proxy API)
|
|
543
|
+
- **Node.js**: 6.0+ for full support, 0.12+ with polyfills
|
|
544
|
+
|
|
545
|
+
### Related Utilities
|
|
546
|
+
|
|
547
|
+
- **Proxy**: JavaScript's built-in proxy API
|
|
548
|
+
- **Symbol**: Used for custom property keys
|
|
549
|
+
- **Object.setPrototypeOf**: For prototype chain manipulation
|
|
550
|
+
|
|
551
|
+
### Changelog
|
|
552
|
+
|
|
553
|
+
**v1.0.0**
|
|
554
|
+
- Initial release
|
|
555
|
+
- Support for all four function overloads
|
|
556
|
+
- Full TypeScript support
|
|
557
|
+
- Proxy-based implementation
|
|
558
|
+
|
|
559
|
+
---
|
|
560
|
+
|
|
561
|
+
This documentation provides a comprehensive guide to using the Indexable utility. The utility is particularly powerful for creating custom data structures that need numeric index access while maintaining full control over the access patterns and maintaining type safety.
|