All files / src/shared types.ts

100% Statements 52/52
100% Branches 15/15
100% Functions 13/13
100% Lines 40/40

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                                                          75x 17x 7x   10x     75x 84x     75x 91x     75x 17x     75x 29x     75x 12x     75x 18x     75x         6x     75x     521x     75x     152x 9x 9x 6x   3x         75x 75x 75x 75x   75x 75x   75x 75x   75x       19x       19x 220x 111x     19x    
export type JsonValue = string | number | boolean | null;
 
declare const __brand: unique symbol;
type Brand<B> = { [__brand]: B };
export type Branded<T, B> = T & Brand<B>;
 
export type DeepReadonly<T> = {
  readonly [Key in keyof T]: T[Key] extends unknown[] | Record<string, unknown>
    ? DeepReadonly<T[Key]>
    : T[Key];
};
 
export type Tuple<
  T,
  N extends number,
  R extends unknown[] = [],
> = R["length"] extends N ? R : Tuple<T, N, [T, ...R]>;
 
export type MaybePromise<T> = T | Promise<T>;
 
/* KeysMatching<T, V>
 *
 * Find all keys in T which have the type V
 * e.g. KeysMatching<Actor, boolean> to return "animate" | "isPinned"
 */
export type KeysMatching<T, V> = {
  [K in keyof T]-?: T[K] extends V ? K : never;
}[keyof T];
 
export const isStringArray = (value: unknown): value is string[] => {
  if (!Array.isArray(value)) {
    return false;
  }
  return value.every(isString);
};
 
export const isString = (value: unknown): value is string => {
  return typeof value === "string";
};
 
export const isNumber = (value: unknown): value is number => {
  return typeof value === "number" && !isNaN(value) && isFinite(value);
};
 
export const isBoolean = (value: unknown): value is boolean => {
  return typeof value === "boolean";
};
 
export const isUndefined = (value: unknown): value is undefined => {
  return value === undefined;
};
 
export const isMaybeString = (value: unknown): value is string | undefined => {
  return isString(value) || isUndefined(value);
};
 
export const isMaybeNumber = (value: unknown): value is number | undefined => {
  return isNumber(value) || isUndefined(value);
};
 
export const ensureType = <T>(
  value: unknown,
  fallback: T,
  isType: (value: unknown) => value is T,
): T => {
  return isType(value) ? value : fallback;
};
 
export const ensureTypeGenerator = <T>(
  isType: (value: unknown) => value is T,
): ((value: unknown, fallback: T) => T) => {
  return (value: unknown, fallback: T) => (isType(value) ? value : fallback);
};
 
export const ensurePromisedTypeGenerator = <T>(
  isType: (value: unknown) => value is T,
): ((promise: Promise<unknown>, fallback: T) => Promise<T>) => {
  return async (promise: Promise<unknown>, fallback: T): Promise<T> => {
    try {
      const value = await promise;
      return isType(value) ? value : fallback;
    } catch {
      return fallback;
    }
  };
};
 
export const ensureString = ensureTypeGenerator(isString);
export const ensureStringArray = ensureTypeGenerator(isStringArray);
export const ensureNumber = ensureTypeGenerator(isNumber);
export const ensureBoolean = ensureTypeGenerator(isBoolean);
 
export const ensurePromisedString = ensurePromisedTypeGenerator(isString);
export const ensurePromisedNumber = ensurePromisedTypeGenerator(isNumber);
 
export const ensureMaybeString = ensureTypeGenerator(isMaybeString);
export const ensureMaybeNumber = ensureTypeGenerator(isMaybeNumber);
 
export const omit = <T, O extends keyof T>(
  obj: T,
  ...keys: O[]
): Omit<T, O> => {
  const ret = {} as {
    [K in keyof typeof obj]: (typeof obj)[K];
  };
  let key: keyof typeof obj;
  for (key in obj) {
    if (!keys.includes(key as O)) {
      ret[key] = obj[key];
    }
  }
  return ret;
};