shared-utils.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import { CustomFieldConfig } from './generated-types';
  2. /**
  3. * Predicate with type guard, used to filter out null or undefined values
  4. * in a filter operation.
  5. */
  6. export function notNullOrUndefined<T>(val: T | undefined | null): val is T {
  7. return val !== undefined && val !== null;
  8. }
  9. /**
  10. * Used in exhaustiveness checks to assert a codepath should never be reached.
  11. */
  12. export function assertNever(value: never): never {
  13. throw new Error(`Expected never, got ${typeof value} (${JSON.stringify(value)})`);
  14. }
  15. /**
  16. * Simple object check.
  17. * From https://stackoverflow.com/a/34749873/772859
  18. */
  19. export function isObject(item: any): item is object {
  20. return item && typeof item === 'object' && !Array.isArray(item);
  21. }
  22. export function isClassInstance(item: any): boolean {
  23. // Even if item is an object, it might not have a constructor as in the
  24. // case when it is a null-prototype object, i.e. created using `Object.create(null)`.
  25. return isObject(item) && item.constructor && item.constructor.name !== 'Object';
  26. }
  27. type NumericPropsOf<T> = {
  28. [K in keyof T]: T[K] extends number ? K : never;
  29. }[keyof T];
  30. // homomorphic helper type
  31. // From https://stackoverflow.com/a/56140392/772859
  32. type NPO<T, KT extends keyof T> = {
  33. [K in KT]: T[K] extends string | number | boolean
  34. ? T[K]
  35. : T[K] extends Array<infer A>
  36. ? Array<OnlyNumerics<A>>
  37. : OnlyNumerics<T[K]>;
  38. };
  39. // quick abort if T is a function or primitive
  40. // otherwise pass to a homomorphic helper type
  41. type OnlyNumerics<T> = NPO<T, NumericPropsOf<T>>;
  42. /**
  43. * Adds up all the values of a given numeric property of a list of objects.
  44. */
  45. export function summate<T extends OnlyNumerics<T>>(
  46. items: T[] | undefined | null,
  47. prop: keyof OnlyNumerics<T>,
  48. ): number {
  49. return (items || []).reduce((sum, i) => sum + (i[prop] as unknown as number), 0);
  50. }
  51. /**
  52. * Given an array of option arrays `[['red, 'blue'], ['small', 'large']]`, this method returns a new array
  53. * containing all the combinations of those options:
  54. *
  55. * @example
  56. * ```
  57. * generateAllCombinations([['red, 'blue'], ['small', 'large']]);
  58. * // =>
  59. * // [
  60. * // ['red', 'small'],
  61. * // ['red', 'large'],
  62. * // ['blue', 'small'],
  63. * // ['blue', 'large'],
  64. * // ]
  65. */
  66. export function generateAllCombinations<T>(
  67. optionGroups: T[][],
  68. combination: T[] = [],
  69. k: number = 0,
  70. output: T[][] = [],
  71. ): T[][] {
  72. if (k === 0) {
  73. optionGroups = optionGroups.filter(g => 0 < g.length);
  74. }
  75. if (k === optionGroups.length) {
  76. output.push(combination);
  77. return [];
  78. } else {
  79. /* eslint-disable @typescript-eslint/prefer-for-of */
  80. for (let i = 0; i < optionGroups[k].length; i++) {
  81. generateAllCombinations(optionGroups, combination.concat(optionGroups[k][i]), k + 1, output);
  82. }
  83. /* eslint-enable @typescript-eslint/prefer-for-of */
  84. return output;
  85. }
  86. }
  87. /**
  88. * @description
  89. * Returns the input field name of a custom field, taking into account that "relation" type custom
  90. * field inputs are suffixed with "Id" or "Ids".
  91. */
  92. export function getGraphQlInputName(config: { name: string; type: string; list?: boolean }): string {
  93. if (config.type === 'relation') {
  94. return config.list === true ? `${config.name}Ids` : `${config.name}Id`;
  95. } else {
  96. return config.name;
  97. }
  98. }