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.
Files changed (82) hide show
  1. package/README.md +150 -0
  2. package/dist/chunks/decorator-BXsign4Z.js +176 -0
  3. package/dist/chunks/decorator-BXsign4Z.js.map +1 -0
  4. package/dist/chunks/decorator-CPbZNnsX.esm.js +168 -0
  5. package/dist/chunks/decorator-CPbZNnsX.esm.js.map +1 -0
  6. package/dist/decorator.d.ts +50 -0
  7. package/dist/decorator.esm.js +2 -0
  8. package/dist/decorator.esm.js.map +1 -0
  9. package/dist/decorator.js +11 -0
  10. package/dist/decorator.js.map +1 -0
  11. package/dist/destroyable.d.ts +48 -0
  12. package/dist/destroyable.esm.js +91 -0
  13. package/dist/destroyable.esm.js.map +1 -0
  14. package/dist/destroyable.js +98 -0
  15. package/dist/destroyable.js.map +1 -0
  16. package/dist/eventful.d.ts +11 -0
  17. package/dist/eventful.esm.js +88 -0
  18. package/dist/eventful.esm.js.map +1 -0
  19. package/dist/eventful.js +90 -0
  20. package/dist/eventful.js.map +1 -0
  21. package/dist/index.d.ts +15 -0
  22. package/dist/index.esm.js +7 -0
  23. package/dist/index.esm.js.map +1 -0
  24. package/dist/index.js +52 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/indexable.d.ts +31 -0
  27. package/dist/indexable.esm.js +85 -0
  28. package/dist/indexable.esm.js.map +1 -0
  29. package/dist/indexable.js +89 -0
  30. package/dist/indexable.js.map +1 -0
  31. package/dist/mutts.umd.js +2 -0
  32. package/dist/mutts.umd.js.map +1 -0
  33. package/dist/mutts.umd.min.js +2 -0
  34. package/dist/mutts.umd.min.js.map +1 -0
  35. package/dist/promiseChain.d.ts +11 -0
  36. package/dist/promiseChain.esm.js +72 -0
  37. package/dist/promiseChain.esm.js.map +1 -0
  38. package/dist/promiseChain.js +74 -0
  39. package/dist/promiseChain.js.map +1 -0
  40. package/dist/reactive.d.ts +114 -0
  41. package/dist/reactive.esm.js +1455 -0
  42. package/dist/reactive.esm.js.map +1 -0
  43. package/dist/reactive.js +1472 -0
  44. package/dist/reactive.js.map +1 -0
  45. package/dist/std-decorators.d.ts +17 -0
  46. package/dist/std-decorators.esm.js +161 -0
  47. package/dist/std-decorators.esm.js.map +1 -0
  48. package/dist/std-decorators.js +169 -0
  49. package/dist/std-decorators.js.map +1 -0
  50. package/docs/decorator.md +300 -0
  51. package/docs/destroyable.md +294 -0
  52. package/docs/events.md +225 -0
  53. package/docs/indexable.md +561 -0
  54. package/docs/promiseChain.md +218 -0
  55. package/docs/reactive.md +2072 -0
  56. package/docs/std-decorators.md +558 -0
  57. package/package.json +132 -0
  58. package/src/decorator.test.ts +495 -0
  59. package/src/decorator.ts +205 -0
  60. package/src/destroyable.test.ts +155 -0
  61. package/src/destroyable.ts +158 -0
  62. package/src/eventful.test.ts +380 -0
  63. package/src/eventful.ts +69 -0
  64. package/src/index.ts +7 -0
  65. package/src/indexable.test.ts +388 -0
  66. package/src/indexable.ts +124 -0
  67. package/src/promiseChain.test.ts +201 -0
  68. package/src/promiseChain.ts +99 -0
  69. package/src/reactive/array.test.ts +923 -0
  70. package/src/reactive/array.ts +352 -0
  71. package/src/reactive/core.test.ts +1663 -0
  72. package/src/reactive/core.ts +866 -0
  73. package/src/reactive/index.ts +28 -0
  74. package/src/reactive/interface.test.ts +1477 -0
  75. package/src/reactive/interface.ts +231 -0
  76. package/src/reactive/map.test.ts +866 -0
  77. package/src/reactive/map.ts +162 -0
  78. package/src/reactive/set.test.ts +289 -0
  79. package/src/reactive/set.ts +142 -0
  80. package/src/std-decorators.test.ts +679 -0
  81. package/src/std-decorators.ts +182 -0
  82. package/src/utils.ts +52 -0
@@ -0,0 +1,69 @@
1
+ export class Eventful<Events extends Record<string, (...args: any[]) => void>> {
2
+ readonly #events = new Map<keyof Events, ((...args: any[]) => void)[]>()
3
+ readonly #hooks = [] as ((...args: any[]) => void)[]
4
+
5
+ public hook(
6
+ cb: <EventType extends keyof Events>(
7
+ event: EventType,
8
+ ...args: Parameters<Events[EventType]>
9
+ ) => void
10
+ ): () => void {
11
+ if (!this.#hooks.includes(cb)) this.#hooks.push(cb)
12
+ return () => {
13
+ this.#hooks.splice(this.#hooks.indexOf(cb), 1)
14
+ }
15
+ }
16
+
17
+ public on(events: Partial<Events>): void
18
+ public on<EventType extends keyof Events>(event: EventType, cb: Events[EventType]): () => void
19
+ public on<EventType extends keyof Events>(
20
+ eventOrEvents: EventType | Partial<Events>,
21
+ cb?: Events[EventType]
22
+ ): () => void {
23
+ if (typeof eventOrEvents === 'object') {
24
+ for (const e of Object.keys(eventOrEvents) as (keyof Events)[]) {
25
+ this.on(e, eventOrEvents[e]!)
26
+ }
27
+ } else if (cb !== undefined) {
28
+ let callbacks = this.#events.get(eventOrEvents)
29
+ if (!callbacks) {
30
+ callbacks = []
31
+ this.#events.set(eventOrEvents, callbacks)
32
+ }
33
+ callbacks.push(cb)
34
+ }
35
+ // @ts-expect-error Generic case leads to generic case
36
+ return () => this.off(eventOrEvents, cb)
37
+ }
38
+ public off(events: Partial<Events>): void
39
+ public off<EventType extends keyof Events>(event: EventType, cb?: Events[EventType]): void
40
+ public off<EventType extends keyof Events>(
41
+ eventOrEvents: EventType | Partial<Events>,
42
+ cb?: Events[EventType]
43
+ ): void {
44
+ if (typeof eventOrEvents === 'object') {
45
+ for (const e of Object.keys(eventOrEvents) as (keyof Events)[]) {
46
+ this.off(e, eventOrEvents[e])
47
+ }
48
+ } else if (cb !== null && cb !== undefined) {
49
+ const callbacks = this.#events.get(eventOrEvents)
50
+ if (callbacks) {
51
+ this.#events.set(
52
+ eventOrEvents,
53
+ callbacks.filter((c) => c !== cb)
54
+ )
55
+ }
56
+ } else {
57
+ // Remove all listeners for this event
58
+ this.#events.delete(eventOrEvents)
59
+ }
60
+ }
61
+ public emit<EventType extends keyof Events>(
62
+ event: EventType,
63
+ ...args: Parameters<Events[EventType]>
64
+ ) {
65
+ const callbacks = this.#events.get(event)
66
+ if (callbacks) for (const cb of callbacks) cb.apply(this, args)
67
+ for (const cb of this.#hooks) cb.call(this, event, ...args)
68
+ }
69
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './decorator'
2
+ export * from './destroyable'
3
+ export * from './eventful'
4
+ export * from './indexable'
5
+ export * from './reactive'
6
+ export * from './std-decorators'
7
+ export * from './utils'
@@ -0,0 +1,388 @@
1
+ import { getAt, Indexable, setAt } from './indexable'
2
+
3
+ // TODO: get/set became this: based
4
+ describe('Indexable', () => {
5
+ describe('Indexable(base, accessor)', () => {
6
+ it('should create indexable class with custom accessor', () => {
7
+ class Base {
8
+ constructor(public items: string[]) {}
9
+ }
10
+
11
+ const IndexableBase = Indexable(Base, {
12
+ get: function (this: Base, index) {
13
+ return this.items[index]
14
+ },
15
+ set: function (this: Base, index, value) {
16
+ this.items[index] = value
17
+ },
18
+ })
19
+ const instance = new IndexableBase(['a', 'b', 'c'])
20
+
21
+ expect(instance[0]).toBe('a')
22
+ expect(instance[1]).toBe('b')
23
+ expect(instance[2]).toBe('c')
24
+ expect(instance.items).toEqual(['a', 'b', 'c'])
25
+ })
26
+
27
+ it('should handle different item types', () => {
28
+ class Base {
29
+ constructor(public numbers: number[]) {}
30
+ }
31
+
32
+ const IndexableBase = Indexable(Base, {
33
+ get: function (this: Base, index) {
34
+ return this.numbers[index] * 2
35
+ },
36
+ set: function (this: Base, index, value) {
37
+ this.numbers[index] = value / 2
38
+ },
39
+ })
40
+ const instance = new IndexableBase([1, 2, 3])
41
+
42
+ expect(instance[0]).toBe(2)
43
+ expect(instance[1]).toBe(4)
44
+ expect(instance[2]).toBe(6)
45
+ })
46
+
47
+ it('should preserve base class methods', () => {
48
+ class Base {
49
+ constructor(public items: string[]) {}
50
+ getLength() {
51
+ return this.items.length
52
+ }
53
+ }
54
+
55
+ const IndexableBase = Indexable(Base, {
56
+ get: function (this: Base, index) {
57
+ return this.items[index]
58
+ },
59
+ set: function (this: Base, index, value) {
60
+ this.items[index] = value
61
+ },
62
+ })
63
+ const instance = new IndexableBase(['a', 'b'])
64
+
65
+ expect(instance.getLength()).toBe(2)
66
+ expect(instance[0]).toBe('a')
67
+ })
68
+ })
69
+
70
+ describe('Indexable(base with getAt and setAt methods)', () => {
71
+ it('should use the base class getAt and setAt methods', () => {
72
+ class Base {
73
+ constructor(public items: string[]) {}
74
+ [getAt](index: number): string {
75
+ return this.items[index]
76
+ }
77
+ [setAt](index: number, value: string): void {
78
+ this.items[index] = value
79
+ }
80
+ }
81
+
82
+ const IndexableBase = Indexable(Base)
83
+ const instance = new IndexableBase(['x', 'y', 'z'])
84
+
85
+ expect(instance[0]).toBe('x')
86
+ instance[0] = 'a'
87
+ expect(instance[0]).toBe('a')
88
+ expect(instance.items[0]).toBe('a')
89
+ })
90
+
91
+ it('should work with custom getAt and setAt implementation', () => {
92
+ class Base {
93
+ constructor(public numbers: number[]) {}
94
+ [getAt](index: number): number {
95
+ return this.numbers[index] * 3
96
+ }
97
+ [setAt](index: number, value: number): void {
98
+ this.numbers[index] = value / 3
99
+ }
100
+ }
101
+
102
+ const IndexableBase = Indexable(Base)
103
+ const instance = new IndexableBase([1, 2, 3])
104
+
105
+ expect(instance[0]).toBe(3)
106
+ instance[0] = 9
107
+ expect(instance[0]).toBe(9)
108
+ expect(instance.numbers[0]).toBe(3)
109
+ })
110
+
111
+ it('should throw when setAt method is missing', () => {
112
+ class Base {
113
+ constructor(public items: string[]) {}
114
+ [getAt](index: number): string {
115
+ return this.items[index]
116
+ }
117
+ // Missing setAt method
118
+ }
119
+
120
+ const IndexableBase = Indexable(Base)
121
+ const instance = new IndexableBase(['x', 'y'])
122
+
123
+ expect(instance[0]).toBe('x')
124
+ expect(() => {
125
+ instance[0] = 'z'
126
+ }).toThrow('Indexable class has read-only numeric index access')
127
+ })
128
+ })
129
+
130
+ describe('Indexable()', () => {
131
+ it('should create abstract class with abstract getAt method', () => {
132
+ const AbstractIndexable = Indexable<string>()
133
+
134
+ //@ts-expect-error Should be abstract
135
+ void new AbstractIndexable()
136
+
137
+ // Should have abstract getAt method
138
+ class Concrete extends AbstractIndexable {
139
+ constructor(private items: string[]) {
140
+ super()
141
+ }
142
+ [getAt](index: number): string {
143
+ return this.items[index]
144
+ }
145
+ }
146
+
147
+ const instance = new Concrete(['p', 'q', 'r'])
148
+ expect(instance[0]).toBe('p')
149
+ expect(instance[1]).toBe('q')
150
+ expect(instance[2]).toBe('r')
151
+ })
152
+
153
+ it('should enforce getAt method implementation', () => {
154
+ const AbstractIndexable = Indexable<number>()
155
+ //@ts-expect-error Should be abstract
156
+ class Invalid extends AbstractIndexable {}
157
+
158
+ // JavaScript doesn't enforce abstract methods at runtime
159
+ // So instantiation won't throw, but calling the missing method will
160
+ const instance = new Invalid()
161
+ expect(() => instance[0]).toThrow()
162
+ })
163
+ })
164
+
165
+ describe('edge cases', () => {
166
+ it('should handle out of bounds access', () => {
167
+ class Base {
168
+ constructor(public items: string[]) {}
169
+ }
170
+
171
+ const IndexableBase = Indexable(Base, {
172
+ get: function (this: Base, index) {
173
+ return this.items[index]
174
+ },
175
+ set: function (this: Base, index, value) {
176
+ this.items[index] = value
177
+ },
178
+ })
179
+ const instance = new IndexableBase(['a', 'b'])
180
+
181
+ expect(instance[5]).toBeUndefined()
182
+ expect(instance[-1]).toBeUndefined()
183
+ })
184
+
185
+ it('should handle empty arrays', () => {
186
+ class Base {
187
+ constructor(public items: string[]) {}
188
+ }
189
+
190
+ const IndexableBase = Indexable(Base, {
191
+ get: function (this: Base, index) {
192
+ return this.items[index]
193
+ },
194
+ set: function (this: Base, index, value) {
195
+ this.items[index] = value
196
+ },
197
+ })
198
+ const instance = new IndexableBase([])
199
+
200
+ expect(instance[0]).toBeUndefined()
201
+ })
202
+
203
+ it('should not interfere with non-numeric properties', () => {
204
+ class Base {
205
+ constructor(public items: string[]) {}
206
+ length = 42
207
+ }
208
+
209
+ const IndexableBase = Indexable(Base, {
210
+ get: function (this: Base, index) {
211
+ return this.items[index]
212
+ },
213
+ set: function (this: Base, index, value) {
214
+ this.items[index] = value
215
+ },
216
+ })
217
+ const instance = new IndexableBase(['a', 'b'])
218
+
219
+ expect(instance.length).toBe(42)
220
+ expect(instance.items).toEqual(['a', 'b'])
221
+ })
222
+
223
+ it('should not interfere with setting non-numeric properties', () => {
224
+ class Base {
225
+ constructor(public items: string[]) {}
226
+ length = 42
227
+ }
228
+
229
+ const IndexableBase = Indexable(Base, {
230
+ get: function (this: Base, index) {
231
+ return this.items[index]
232
+ },
233
+ set: function (this: Base, index, value) {
234
+ this.items[index] = value
235
+ },
236
+ })
237
+ const instance = new IndexableBase(['a', 'b'])
238
+
239
+ instance.length = 100
240
+ expect(instance.length).toBe(100)
241
+ })
242
+ })
243
+
244
+ describe('integration tests', () => {
245
+ it('should work with complex objects', () => {
246
+ class Person {
247
+ constructor(
248
+ public name: string,
249
+ public age: number
250
+ ) {}
251
+ }
252
+
253
+ class PersonList {
254
+ constructor(public people: Person[]) {}
255
+ [getAt](index: number): Person {
256
+ return this.people[index]
257
+ }
258
+ [setAt](index: number, person: Person): void {
259
+ this.people[index] = person
260
+ }
261
+ }
262
+
263
+ const IndexablePersonList = Indexable(PersonList)
264
+ const people = [new Person('Alice', 30), new Person('Bob', 25), new Person('Charlie', 35)]
265
+ const instance = new IndexablePersonList(people)
266
+
267
+ expect((instance[0] as Person).name).toBe('Alice')
268
+ instance[0] = new Person('Alice Updated', 31)
269
+ expect((instance[0] as Person).name).toBe('Alice Updated')
270
+ expect((instance[0] as Person).age).toBe(31)
271
+ expect(instance.people[0].name).toBe('Alice Updated')
272
+ })
273
+
274
+ it('should support multiple inheritance levels', () => {
275
+ class Base {
276
+ constructor(public items: string[]) {}
277
+ }
278
+
279
+ class Extended extends Indexable(Base, {
280
+ get: function (this: Base, index) {
281
+ return this.items[index]
282
+ },
283
+ set: function (this: Base, index, value) {
284
+ this.items[index] = value
285
+ },
286
+ }) {
287
+ constructor(
288
+ items: string[],
289
+ public extra: string
290
+ ) {
291
+ super(items)
292
+ }
293
+ }
294
+
295
+ const instance = new Extended(['a', 'b'], 'extra')
296
+ expect(instance[0]).toBe('a')
297
+ expect(instance.extra).toBe('extra')
298
+ })
299
+
300
+ it('should support setting with custom logic', () => {
301
+ class Base {
302
+ constructor(public items: string[]) {}
303
+ }
304
+
305
+ const IndexableBase = Indexable(Base, {
306
+ get: function (this: Base, index) {
307
+ return this.items[index]
308
+ },
309
+ set: function (this: Base, index, value) {
310
+ // Custom logic: convert to uppercase
311
+ this.items[index] = value.toUpperCase()
312
+ },
313
+ })
314
+ const instance = new IndexableBase(['a', 'b', 'c'])
315
+
316
+ instance[1] = 'x'
317
+ expect(instance[1]).toBe('X')
318
+ expect(instance.items[1]).toBe('X')
319
+ })
320
+ })
321
+
322
+ describe('Indexable(accessor)', () => {
323
+ it('should create indexable object with custom accessor', () => {
324
+ const IndexableObj = Indexable({
325
+ get: function (this: any, index: number) {
326
+ return this._arr?.[index]
327
+ },
328
+ set: function (this: any, index: number, value: any) {
329
+ if (!this._arr) this._arr = []
330
+ this._arr[index] = value
331
+ },
332
+ })
333
+ const instance = new IndexableObj() as any
334
+ instance._arr = ['a', 'b', 'c']
335
+ expect(instance[0]).toBe('a')
336
+ expect(instance[1]).toBe('b')
337
+ expect(instance[2]).toBe('c')
338
+ })
339
+
340
+ it('should create indexable object with custom getter and setter', () => {
341
+ const IndexableObj = Indexable({
342
+ get: function (this: any, index: number) {
343
+ return this._arr?.[index]
344
+ },
345
+ set: function (this: any, index: number, value: string) {
346
+ if (!this._arr) this._arr = []
347
+ this._arr[index] = value
348
+ },
349
+ })
350
+ const instance = new IndexableObj() as any
351
+ instance[0] = 'x'
352
+ instance[1] = 'y'
353
+ expect(instance[0]).toBe('x')
354
+ expect(instance[1]).toBe('y')
355
+ expect(instance._arr[0]).toBe('x')
356
+ expect(instance._arr[1]).toBe('y')
357
+ })
358
+
359
+ it('should support transformation in setter', () => {
360
+ const IndexableObj = Indexable({
361
+ get: function (this: any, index: number) {
362
+ return this._arr?.[index] * 2
363
+ },
364
+ set: function (this: any, index: number, value: number) {
365
+ if (!this._arr) this._arr = []
366
+ this._arr[index] = value / 2
367
+ },
368
+ })
369
+ const instance = new IndexableObj() as any
370
+ instance._arr = [1, 2, 3]
371
+ instance[0] = 10
372
+ expect(instance[0]).toBe(10)
373
+ expect(instance._arr[0]).toBe(5)
374
+ })
375
+
376
+ it('should throw if setter is missing and assignment is attempted', () => {
377
+ const IndexableObj = Indexable({
378
+ get: function (this: any, index: number) {
379
+ return this._arr?.[index]
380
+ },
381
+ })
382
+ const instance = new IndexableObj() as any
383
+ expect(() => {
384
+ instance[0] = 'fail'
385
+ }).toThrow('Indexable class has read-only numeric index access')
386
+ })
387
+ })
388
+ })
@@ -0,0 +1,124 @@
1
+ export const getAt = Symbol('getAt')
2
+ export const setAt = Symbol('setAt')
3
+
4
+ interface IndexingAt<Items = any> {
5
+ [getAt](index: number): Items
6
+ }
7
+
8
+ interface Accessor<T, Items> {
9
+ get(this: T, index: number): Items
10
+ set?(this: T, index: number, value: Items): void
11
+ getLength?(this: T): number
12
+ setLength?(this: T, value: number): void
13
+ }
14
+
15
+ abstract class AbstractGetAt<Items = any> {
16
+ abstract [getAt](index: number): Items
17
+ }
18
+
19
+ export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
20
+ base: Base,
21
+ accessor: Accessor<InstanceType<Base>, Items>
22
+ ): new (
23
+ ...args: ConstructorParameters<Base>
24
+ ) => InstanceType<Base> & { [x: number]: Items }
25
+
26
+ export function Indexable<Items>(accessor: Accessor<any, Items>): new () => { [x: number]: Items }
27
+
28
+ export function Indexable<Base extends new (...args: any[]) => IndexingAt>(
29
+ base: Base
30
+ ): new (
31
+ ...args: ConstructorParameters<Base>
32
+ ) => InstanceType<Base> & { [x: number]: AtReturnType<InstanceType<Base>> }
33
+
34
+ export function Indexable<Items>(): abstract new (
35
+ ...args: any[]
36
+ ) => AbstractGetAt & { [x: number]: Items }
37
+
38
+ export function Indexable<Items, Base extends abstract new (...args: any[]) => any>(
39
+ base?: Base | Accessor<Base, Items>,
40
+ accessor?: Accessor<Base, Items>
41
+ ) {
42
+ if (base && typeof base !== 'function') {
43
+ accessor = base as Accessor<Base, Items>
44
+ base = undefined
45
+ }
46
+ if (!base) {
47
+ //@ts-expect-error
48
+ base = class {} as Base
49
+ }
50
+ if (!accessor) {
51
+ accessor = {
52
+ get(this: any, index: number) {
53
+ if (typeof this[getAt] !== 'function') {
54
+ throw new Error('Indexable class must have an [getAt] method')
55
+ }
56
+ return this[getAt](index)
57
+ },
58
+ set(this: any, index: number, value: Items) {
59
+ if (typeof this[setAt] !== 'function') {
60
+ throw new Error('Indexable class has read-only numeric index access')
61
+ }
62
+ this[setAt](index, value)
63
+ },
64
+ }
65
+ }
66
+
67
+ abstract class Indexable extends (base as Base) {
68
+ [x: number]: Items
69
+ }
70
+
71
+ Object.setPrototypeOf(
72
+ Indexable.prototype,
73
+ new Proxy((base as Base).prototype, {
74
+ //@ts-expect-error
75
+ [Symbol.toStringTag]: 'MutTs Indexable',
76
+ get(target, prop, receiver) {
77
+ if (prop in target) {
78
+ const getter = Object.getOwnPropertyDescriptor(target, prop)?.get
79
+ return getter ? getter.call(receiver) : target[prop]
80
+ }
81
+ if (typeof prop === 'string') {
82
+ if (prop === 'length' && accessor.getLength) return accessor.getLength.call(receiver)
83
+ const numProp = Number(prop)
84
+ if (!Number.isNaN(numProp)) {
85
+ return accessor.get!.call(receiver, numProp) as Items
86
+ }
87
+ }
88
+ return undefined
89
+ },
90
+ set(target, prop, value, receiver) {
91
+ if (prop in target) {
92
+ const setter = Object.getOwnPropertyDescriptor(target, prop)?.set
93
+ if (setter) setter.call(receiver, value)
94
+ else target[prop] = value
95
+ return true
96
+ }
97
+ if (typeof prop === 'string') {
98
+ if (prop === 'length' && accessor.setLength) {
99
+ accessor.setLength.call(receiver, value)
100
+ return true
101
+ }
102
+ const numProp = Number(prop)
103
+ if (!Number.isNaN(numProp)) {
104
+ if (!accessor.set) {
105
+ throw new Error('Indexable class has read-only numeric index access')
106
+ }
107
+ accessor.set!.call(receiver, numProp, value)
108
+ return true
109
+ }
110
+ }
111
+ Object.defineProperty(receiver, prop, {
112
+ value,
113
+ writable: true,
114
+ enumerable: true,
115
+ configurable: true,
116
+ })
117
+ return true
118
+ },
119
+ })
120
+ )
121
+ return Indexable
122
+ }
123
+
124
+ type AtReturnType<T> = T extends { [getAt](index: number): infer R } ? R : never