All files util.ts

100% Statements 21/21
100% Branches 14/14
100% Functions 5/5
100% Lines 21/21

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            6x                                                       6x   61x 61x     57x 247x     240x       61x               6x 96x   96x 17x 79x 19x     80x 80x     60x             6x 57x                   6x 1x    
/**
 * This module is not part of the public API!
 */
 
/* API Barrier */
 
import { strict as assert } from 'assert';
 
/**
 * Type that consists of the union of all properties that are marked as optional through a question mark.
 *
 * Note that properties that have undefined in their domain, but no question mark next to the property name are *not*
 * included. Also note that, in strict compilation mode, TypeScript will add undefined to the domain of the property if
 * there is a question mark next to the property name.
 *
 * @typeparam T generic type parameter
 */
export type OptionalPropertyNames<T extends {}> = {[K in keyof T]-?: {} extends {[_ in K]: T[K]} ? K : never}[keyof T];
export type Defined<T> = T extends undefined ? never : T;
export type OnlyOptionals<T extends {}> = {[K in OptionalPropertyNames<T>]: Defined<T[K]>};
 
/**
 * Copy the values of all string-keyed enumerable own properties from the source object to the target object.
 *
 * Note the differences to
 * [`Object.assign()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign):
 * - Only properties with `string` keys are copied.
 * - A property of the source value that has the value `undefined` is copied only if the property key is not yet in the
 *   target. (The check is performed using the `in` operator.)
 *
 * @param target the target object
 * @param source the source object
 * @return the target object
 */
export function assignDefined<T extends {[key: string]: any}, U extends {[key: string]: any}>(
    target: T, source: U | undefined): T & U {
  const typedTarget: T & U = target as T & U;
  if (source !== undefined) {
    // Object.entries() returns “a given object's own enumerable string-keyed property [key, value] pairs,”
    // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
    for (const [key, value] of Object.entries(source)) {
      if (!(key in target) || value !== undefined) {
        // Casting of key necessary for type soundness:
        // https://github.com/microsoft/TypeScript/issues/31661#issuecomment-497474815
        typedTarget[key as keyof U] = source[key];
      }
    }
  }
  return typedTarget;
}
 
/**
 * Returns a deep clone of the given object.
 *
 * This function covers only what is needed in this project! It is not an equivalent to a library function.
 */
export function deepClone<T>(original: T): T {
  assert(['undefined', 'object', 'boolean', 'number', 'string'].includes(typeof original),
      'Value with unsupported type in deepClone()');
  if (Array.isArray(original)) {
    return original.map(deepClone) as T & any[];
  } else if (typeof original === 'object' && original !== null) {
    return Object.entries(original).reduce((obj, [key, value]) => {
      // Casting of key necessary for type soundness:
      // https://github.com/microsoft/TypeScript/issues/31661#issuecomment-497474815
      obj[key as keyof T] = deepClone(value);
      return obj;
    }, {} as T & {[key: string]: any});
  } else {
    return original;
  }
}
 
/**
 * Returns the first argument that is defined, or undefined if none of the arguments is defined.
 */
export function coalesce<T>(left: T | undefined, right: T): T {
  return left === undefined ? right : left;
}
 
/**
 * Function that always throws an error.
 *
 * The purpose of this function is to be used as a compile-time type completeness check; for instance, in a `switch`
 * statement. Calling this function will cause no error *only if* control-flow-based type analysis infers the argument
 * type as `never` – in other words, if the function call cannot be reached.
 */
export function unreachableCase(x: never): never {
  throw new Error(`Unexpected case that should be unreachable: ${x}`);
}