All files pathOr.ts

100% Statements 10/10
100% Branches 4/4
100% Functions 2/2
100% Lines 9/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 1531x                                                                                                                                                                                                                                                                                   1x 9x       9x 16x 16x 6x   10x   3x    
import { purry } from './purry';
import { NonNull, Key } from './_types';
 
/**
 * Given a union of indexable types `T`, we derive an indexable type
 * containing all of the keys of each variant of `T`. If a key is
 * present in multiple variants of `T`, then the corresponding type in
 * `Pathable<T>` will be the intersection of all types for that key.
 * @example
 *    type T1 = Pathable<{a: number} | {a: string; b: boolean}>
 *    // {a: number | string; b: boolean}
 *
 *    type T2 = Pathable<{a?: {b: string}}
 *    // {a: {b: string} | undefined}
 *
 *    type T3 = Pathable<{a: string} | number>
 *    // {a: string}
 *
 *    type T4 = Pathable<{a: number} | {a: string} | {b: boolean}>
 *    // {a: number | string; b: boolean}
 *
 * This type lets us answer the questions:
 * - Given some object of type `T`, what keys might this object have?
 * - If this object did happen to have a particular key, what values
 *   might that key have?
 */
type Pathable<T> = { [K in AllKeys<T>]: TypesForKey<T, K> };
 
type AllKeys<T> = T extends infer I ? keyof I : never;
type TypesForKey<T, K extends Key> = T extends infer I
  ? K extends keyof I ? I[K] : never
  : never;
 
/**
 * Given some `A` which is a key of at least one variant of `T`, derive
 * `T[A]` for the cases where `A` is present in `T`, and `T[A]` is not
 * null or undefined.
 */
type PathValue1<T, A extends keyof Pathable<T>> = NonNull<Pathable<T>>[A];
/** All possible options after successfully reaching `T[A]` */
type Pathable1<T, A extends keyof Pathable<T>> = Pathable<PathValue1<T, A>>;
 
/** As `PathValue1`, but for `T[A][B]` */
type PathValue2<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>
> = NonNull<Pathable1<T, A>>[B];
/** As `Pathable1`, but for `T[A][B]` */
type Pathable2<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>
> = Pathable<PathValue2<T, A, B>>;
 
/** As `PathValue1`, but for `T[A][B][C]` */
type PathValue3<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>,
  C extends keyof Pathable2<T, A, B>
> = NonNull<Pathable2<T, A, B>>[C];
 
/**
 * Gets the value at `path` of `object`. If the resolved value is `undefined`, the `defaultValue` is returned in its place.
 * @param object the target object
 * @param path the path of the property to get
 * @param defaultValue the default value
 * @signature R.pathOr(object, array, defaultValue)
 * @example
 *    R.pathOr({x: 10}, ['y'], 2) // 2
 *    R.pathOr({y: 10}, ['y'], 2) // 10
 * @data_first
 * @category Object
 */
export function pathOr<T, A extends keyof Pathable<T>>(
  object: T,
  path: [A],
  defaultValue: PathValue1<T, A>
): PathValue1<T, A>;
 
export function pathOr<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>
>(
  object: T,
  path: [A, B],
  defaultValue: PathValue2<T, A, B>
): PathValue2<T, A, B>;
 
export function pathOr<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>,
  C extends keyof Pathable2<T, A, B>
>(
  object: T,
  path: [A, B, C],
  defaultValue: PathValue3<T, A, B, C>
): PathValue3<T, A, B, C>;
 
/**
 * Gets the value at `path` of `object`. If the resolved value is `undefined`, the `defaultValue` is returned in its place.
 * @param object the target object
 * @param path the path of the property to get
 * @param defaultValue the default value
 * @signature R.pathOr(array, defaultValue)(object)
 * @example
 *    R.pipe({x: 10}, R.pathOr(['y'], 2)) // 2
 *    R.pipe({y: 10}, R.pathOr(['y'], 2)) // 10
 * @data_last
 * @category Object
 */
export function pathOr<T, A extends keyof Pathable<T>>(
  path: [A],
  defaultValue: PathValue1<T, A>
): (object: T) => PathValue1<T, A>;
 
export function pathOr<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>
>(
  path: [A, B],
  defaultValue: PathValue2<T, A, B>
): (object: T) => PathValue2<T, A, B>;
 
export function pathOr<
  T,
  A extends keyof Pathable<T>,
  B extends keyof Pathable1<T, A>,
  C extends keyof Pathable2<T, A, B>
>(
  path: [A, B, C],
  defaultValue: PathValue3<T, A, B, C>
): (object: T) => PathValue3<T, A, B, C>;
 
export function pathOr() {
  return purry(_pathOr, arguments);
}
 
function _pathOr(object: any, path: any[], defaultValue: any): any {
  let current = object;
  for (const prop of path) {
    if (current == null || current[prop] == null) {
      return defaultValue;
    }
    current = current[prop];
  }
  return current;
}