1
- import { JsonConvertible , JsonStructure , JsonValue } from '@croct/json' ;
1
+ import { JsonConvertible } from '@croct/json' ;
2
2
3
3
/**
4
4
* A value that can be converted to a JSON pointer.
@@ -15,6 +15,31 @@ export type JsonPointerSegment = string | number;
15
15
*/
16
16
export type JsonPointerSegments = JsonPointerSegment [ ] ;
17
17
18
+ /**
19
+ * A record or array representing the root of a structure.
20
+ */
21
+ export type RootStructure = Record < string | number | symbol , any > | any [ ] ;
22
+
23
+ export type RootValue = any ;
24
+
25
+ /**
26
+ * A union of all possible values in a structure.
27
+ */
28
+ export type ReferencedValue < T > = NestedValue < T > ;
29
+
30
+ /**
31
+ * A union of all possible values in a structure, excluding the given type.
32
+ */
33
+ type NestedValue < T , U = never > = T | (
34
+ T extends object
35
+ ? T extends U
36
+ ? never
37
+ : T extends Array < infer I >
38
+ ? NestedValue < I , U | T >
39
+ : T [ keyof T ] | NestedValue < T [ keyof T ] , U | T >
40
+ : never
41
+ ) ;
42
+
18
43
/**
19
44
* An error indicating a problem related to JSON pointer operations.
20
45
*/
@@ -51,7 +76,7 @@ export class InvalidReferenceError extends JsonPointerError {
51
76
/**
52
77
* A key-value pair representing a JSON pointer segment and its value.
53
78
*/
54
- export type Entry = [ JsonPointerSegment | null , JsonValue ] ;
79
+ export type Entry < T > = [ JsonPointerSegment | null , T ] ;
55
80
56
81
/**
57
82
* An RFC 6901-compliant JSON pointer.
@@ -273,15 +298,15 @@ export class JsonPointer implements JsonConvertible {
273
298
/**
274
299
* Returns the value at the referenced location.
275
300
*
276
- * @param {JsonValue } value The value to read from.
301
+ * @param {RootValue } value The value to read from.
277
302
*
278
- * @returns {JsonValue } The value at the referenced location.
303
+ * @returns {ReferencedValue } The value at the referenced location.
279
304
*
280
305
* @throws {InvalidReferenceError } If a numeric segment references a non-array value.
281
306
* @throws {InvalidReferenceError } If a string segment references an array value.
282
307
* @throws {InvalidReferenceError } If there is no value at any level of the pointer.
283
308
*/
284
- public get ( value : JsonValue ) : JsonValue {
309
+ public get < T extends RootValue > ( value : T ) : ReferencedValue < T > {
285
310
const iterator = this . traverse ( value ) ;
286
311
287
312
let result = iterator . next ( ) ;
@@ -304,11 +329,11 @@ export class JsonPointer implements JsonConvertible {
304
329
*
305
330
* This method gracefully handles missing values by returning `false`.
306
331
*
307
- * @param {JsonStructure } root The value to check if the reference exists in.
332
+ * @param {RootValue } root The value to check if the reference exists in.
308
333
*
309
- * @returns {JsonValue } Returns `true` if the value exists, `false` otherwise.
334
+ * @returns {boolean } Returns `true` if the value exists, `false` otherwise.
310
335
*/
311
- public has ( root : JsonStructure ) : boolean {
336
+ public has ( root : RootValue ) : boolean {
312
337
try {
313
338
this . get ( root ) ;
314
339
} catch {
@@ -321,8 +346,8 @@ export class JsonPointer implements JsonConvertible {
321
346
/**
322
347
* Sets the value at the referenced location.
323
348
*
324
- * @param {JsonStructure } root The value to write to.
325
- * @param {JsonValue } value The value to set at the referenced location.
349
+ * @param {RootValue } root The value to write to.
350
+ * @param {unknown } value The value to set at the referenced location.
326
351
*
327
352
* @throws {InvalidReferenceError } If the pointer references the root of the structure.
328
353
* @throws {InvalidReferenceError } If a numeric segment references a non-array value.
@@ -331,17 +356,19 @@ export class JsonPointer implements JsonConvertible {
331
356
* @throws {InvalidReferenceError } If setting the value to an array would cause it to become
332
357
* sparse.
333
358
*/
334
- public set ( root : JsonStructure , value : JsonValue ) : void {
359
+ public set < T extends RootValue > ( root : T , value : unknown ) : void {
335
360
if ( this . isRoot ( ) ) {
336
361
throw new JsonPointerError ( 'Cannot set root value.' ) ;
337
362
}
338
363
339
- const parent = this . getParent ( ) . get ( root ) ;
364
+ const target = this . getParent ( ) . get ( root ) ;
340
365
341
- if ( typeof parent !== 'object' || parent === null ) {
366
+ if ( typeof target !== 'object' || target === null ) {
342
367
throw new JsonPointerError ( `Cannot set value at "${ this . getParent ( ) } ".` ) ;
343
368
}
344
369
370
+ const parent : RootStructure = target ;
371
+
345
372
const segmentIndex = this . segments . length - 1 ;
346
373
const segment = this . segments [ segmentIndex ] ;
347
374
@@ -381,30 +408,32 @@ export class JsonPointer implements JsonConvertible {
381
408
* is a no-op. Pointers referencing array elements remove the element while keeping
382
409
* the array dense.
383
410
*
384
- * @param {JsonStructure } root The value to write to.
411
+ * @param {RootValue } root The value to write to.
385
412
*
386
- * @returns {JsonValue } The unset value, or `undefined` if the referenced location
413
+ * @returns {ReferencedValue|undefined } The unset value, or `undefined` if the referenced location
387
414
* does not exist.
388
415
*
389
416
* @throws {InvalidReferenceError } If the pointer references the root of the root.
390
417
*/
391
- public unset ( root : JsonStructure ) : JsonValue | undefined {
418
+ public unset < T extends RootValue > ( root : T ) : ReferencedValue < T > | undefined {
392
419
if ( this . isRoot ( ) ) {
393
420
throw new InvalidReferenceError ( 'Cannot unset the root value.' ) ;
394
421
}
395
422
396
- let parent : JsonValue ;
423
+ let target : ReferencedValue < T > ;
397
424
398
425
try {
399
- parent = this . getParent ( ) . get ( root ) ;
426
+ target = this . getParent ( ) . get ( root ) ;
400
427
} catch {
401
428
return undefined ;
402
429
}
403
430
404
- if ( typeof parent !== 'object' || parent === null ) {
431
+ if ( typeof target !== 'object' || target === null ) {
405
432
return undefined ;
406
433
}
407
434
435
+ const parent : RootStructure = target ;
436
+
408
437
const segmentIndex = this . segments . length - 1 ;
409
438
const segment = this . segments [ segmentIndex ] ;
410
439
@@ -434,17 +463,17 @@ export class JsonPointer implements JsonConvertible {
434
463
/**
435
464
* Returns an iterator over the stack of values that the pointer references.
436
465
*
437
- * @param {JsonValue } root The value to traverse.
466
+ * @param {RootValue } root The value to traverse.
438
467
*
439
- * @returns {Iterator<JsonPointer > } An iterator over the stack of values that the
468
+ * @returns {Iterator<Entry<ReferencedValue<T> > } An iterator over the stack of values that the
440
469
* pointer references.
441
470
*
442
471
* @throws {InvalidReferenceError } If a numeric segment references a non-array value.
443
472
* @throws {InvalidReferenceError } If a string segment references an array value.
444
473
* @throws {InvalidReferenceError } If there is no value at any level of the pointer.
445
474
*/
446
- public * traverse ( root : JsonValue ) : Iterator < Entry > {
447
- let current : JsonValue = root ;
475
+ public * traverse < T extends RootValue > ( root : T ) : Iterator < Entry < ReferencedValue < T > > > {
476
+ let current : ReferencedValue < T > = root ;
448
477
449
478
yield [ null , current ] ;
450
479
@@ -487,15 +516,13 @@ export class JsonPointer implements JsonConvertible {
487
516
) ;
488
517
}
489
518
490
- const nextValue = current [ segment ] ;
491
-
492
- if ( nextValue === undefined ) {
519
+ if ( ! ( segment in current ) ) {
493
520
throw new InvalidReferenceError (
494
521
`Property "${ segment } " does not exist at "${ this . truncatedAt ( i ) } ".` ,
495
522
) ;
496
523
}
497
524
498
- current = nextValue ;
525
+ current = current [ segment as keyof typeof current ] as ReferencedValue < T > ;
499
526
500
527
yield [ segment , current ] ;
501
528
}
@@ -508,7 +535,7 @@ export class JsonPointer implements JsonConvertible {
508
535
*
509
536
* @returns {boolean } `true` if the pointers are logically equal, `false` otherwise.
510
537
*/
511
- public equals ( other : any ) : other is this {
538
+ public equals ( other : unknown ) : other is JsonPointer {
512
539
if ( this === other ) {
513
540
return true ;
514
541
}
0 commit comments