1- import { JsonConvertible , JsonStructure , JsonValue } from '@croct/json' ;
1+ import { JsonConvertible } from '@croct/json' ;
22
33/**
44 * A value that can be converted to a JSON pointer.
@@ -15,6 +15,31 @@ export type JsonPointerSegment = string | number;
1515 */
1616export type JsonPointerSegments = JsonPointerSegment [ ] ;
1717
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+
1843/**
1944 * An error indicating a problem related to JSON pointer operations.
2045 */
@@ -51,7 +76,7 @@ export class InvalidReferenceError extends JsonPointerError {
5176/**
5277 * A key-value pair representing a JSON pointer segment and its value.
5378 */
54- export type Entry = [ JsonPointerSegment | null , JsonValue ] ;
79+ export type Entry < T > = [ JsonPointerSegment | null , T ] ;
5580
5681/**
5782 * An RFC 6901-compliant JSON pointer.
@@ -273,15 +298,15 @@ export class JsonPointer implements JsonConvertible {
273298 /**
274299 * Returns the value at the referenced location.
275300 *
276- * @param {JsonValue } value The value to read from.
301+ * @param {RootValue } value The value to read from.
277302 *
278- * @returns {JsonValue } The value at the referenced location.
303+ * @returns {ReferencedValue } The value at the referenced location.
279304 *
280305 * @throws {InvalidReferenceError } If a numeric segment references a non-array value.
281306 * @throws {InvalidReferenceError } If a string segment references an array value.
282307 * @throws {InvalidReferenceError } If there is no value at any level of the pointer.
283308 */
284- public get ( value : JsonValue ) : JsonValue {
309+ public get < T extends RootValue > ( value : T ) : ReferencedValue < T > {
285310 const iterator = this . traverse ( value ) ;
286311
287312 let result = iterator . next ( ) ;
@@ -304,11 +329,11 @@ export class JsonPointer implements JsonConvertible {
304329 *
305330 * This method gracefully handles missing values by returning `false`.
306331 *
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.
308333 *
309- * @returns {JsonValue } Returns `true` if the value exists, `false` otherwise.
334+ * @returns {boolean } Returns `true` if the value exists, `false` otherwise.
310335 */
311- public has ( root : JsonStructure ) : boolean {
336+ public has ( root : RootValue ) : boolean {
312337 try {
313338 this . get ( root ) ;
314339 } catch {
@@ -321,8 +346,8 @@ export class JsonPointer implements JsonConvertible {
321346 /**
322347 * Sets the value at the referenced location.
323348 *
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.
326351 *
327352 * @throws {InvalidReferenceError } If the pointer references the root of the structure.
328353 * @throws {InvalidReferenceError } If a numeric segment references a non-array value.
@@ -331,17 +356,19 @@ export class JsonPointer implements JsonConvertible {
331356 * @throws {InvalidReferenceError } If setting the value to an array would cause it to become
332357 * sparse.
333358 */
334- public set ( root : JsonStructure , value : JsonValue ) : void {
359+ public set < T extends RootValue > ( root : T , value : unknown ) : void {
335360 if ( this . isRoot ( ) ) {
336361 throw new JsonPointerError ( 'Cannot set root value.' ) ;
337362 }
338363
339- const parent = this . getParent ( ) . get ( root ) ;
364+ const target = this . getParent ( ) . get ( root ) ;
340365
341- if ( typeof parent !== 'object' || parent === null ) {
366+ if ( typeof target !== 'object' || target === null ) {
342367 throw new JsonPointerError ( `Cannot set value at "${ this . getParent ( ) } ".` ) ;
343368 }
344369
370+ const parent : RootStructure = target ;
371+
345372 const segmentIndex = this . segments . length - 1 ;
346373 const segment = this . segments [ segmentIndex ] ;
347374
@@ -381,30 +408,32 @@ export class JsonPointer implements JsonConvertible {
381408 * is a no-op. Pointers referencing array elements remove the element while keeping
382409 * the array dense.
383410 *
384- * @param {JsonStructure } root The value to write to.
411+ * @param {RootValue } root The value to write to.
385412 *
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
387414 * does not exist.
388415 *
389416 * @throws {InvalidReferenceError } If the pointer references the root of the root.
390417 */
391- public unset ( root : JsonStructure ) : JsonValue | undefined {
418+ public unset < T extends RootValue > ( root : T ) : ReferencedValue < T > | undefined {
392419 if ( this . isRoot ( ) ) {
393420 throw new InvalidReferenceError ( 'Cannot unset the root value.' ) ;
394421 }
395422
396- let parent : JsonValue ;
423+ let target : ReferencedValue < T > ;
397424
398425 try {
399- parent = this . getParent ( ) . get ( root ) ;
426+ target = this . getParent ( ) . get ( root ) ;
400427 } catch {
401428 return undefined ;
402429 }
403430
404- if ( typeof parent !== 'object' || parent === null ) {
431+ if ( typeof target !== 'object' || target === null ) {
405432 return undefined ;
406433 }
407434
435+ const parent : RootStructure = target ;
436+
408437 const segmentIndex = this . segments . length - 1 ;
409438 const segment = this . segments [ segmentIndex ] ;
410439
@@ -434,17 +463,17 @@ export class JsonPointer implements JsonConvertible {
434463 /**
435464 * Returns an iterator over the stack of values that the pointer references.
436465 *
437- * @param {JsonValue } root The value to traverse.
466+ * @param {RootValue } root The value to traverse.
438467 *
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
440469 * pointer references.
441470 *
442471 * @throws {InvalidReferenceError } If a numeric segment references a non-array value.
443472 * @throws {InvalidReferenceError } If a string segment references an array value.
444473 * @throws {InvalidReferenceError } If there is no value at any level of the pointer.
445474 */
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 ;
448477
449478 yield [ null , current ] ;
450479
@@ -487,15 +516,13 @@ export class JsonPointer implements JsonConvertible {
487516 ) ;
488517 }
489518
490- const nextValue = current [ segment ] ;
491-
492- if ( nextValue === undefined ) {
519+ if ( ! ( segment in current ) ) {
493520 throw new InvalidReferenceError (
494521 `Property "${ segment } " does not exist at "${ this . truncatedAt ( i ) } ".` ,
495522 ) ;
496523 }
497524
498- current = nextValue ;
525+ current = current [ segment as keyof typeof current ] as ReferencedValue < T > ;
499526
500527 yield [ segment , current ] ;
501528 }
@@ -508,7 +535,7 @@ export class JsonPointer implements JsonConvertible {
508535 *
509536 * @returns {boolean } `true` if the pointers are logically equal, `false` otherwise.
510537 */
511- public equals ( other : any ) : other is this {
538+ public equals ( other : unknown ) : other is JsonPointer {
512539 if ( this === other ) {
513540 return true ;
514541 }
0 commit comments