Mirror of https://github.com/roostorg/coop
github.com/roostorg/coop
1import { FormInstance } from 'antd';
2import lodashIsPlainObject from 'lodash/isPlainObject';
3import pick from 'lodash/pick';
4import unzip from 'lodash/unzip';
5
6/**
7 * Identical to lodash.pick, except with more type safety.
8 *
9 * Lodash's pick has an overload in the type definition which allows one of its
10 * generic parameters to fall back to being assigned without any constraint,
11 * which defeats type safefty and loses autocomplete. This function just calls
12 * pick, but has a safer signature for type inference.
13 */
14export function safePick<T extends object, U extends keyof T>(
15 obj: T,
16 props: U[],
17) {
18 return pick(obj, props);
19}
20
21export function isPlainObject(
22 obj: unknown,
23): obj is { [k: string | number | symbol]: unknown } {
24 return lodashIsPlainObject(obj);
25}
26
27/**
28 * This is a function that's used to help TS warn us if a union type that we
29 * should've handled all cases for in fact has some cases unhandled.
30 *
31 * After handling all cases, you call `assertUnreachable(unionTypeVar)` and, if
32 * you don't get a compiler error, it means that all the cases have truly been
33 * handled, because TS has narrowed the type of unionTypeVar down to `never`.
34 *
35 * At runtime, this just throws an error, which is appropriate because it should
36 * never be reached.
37 */
38export function assertUnreachable(
39 _x: never,
40 message: string = "Didn't expect to get here",
41): never {
42 throw new Error(message);
43}
44
45/**
46 * A helper for debugging antd forms.
47 *
48 * @returns A pretty-printed JSON string of the form's state, which you can
49 * console.log or render on the screen during dev.
50 */
51export function antdFormState(form: FormInstance, _fieldNames: string[]) {
52 const fieldNamesAndValues = form.getFieldsValue();
53 return JSON.stringify(
54 Object.fromEntries(
55 Object.entries(fieldNamesAndValues).map(([name, value]) => [
56 name,
57 {
58 value,
59 touched: form.isFieldTouched(name),
60 errors: form.getFieldError(name),
61 },
62 ]),
63 ),
64 undefined,
65 4,
66 );
67}
68
69/**
70 * A type-safe wrapper around lodash unzip, that only works for arrays of
71 * 2-tuples, but also handles inverting a `zip([], []) => []`, which callers
72 * rely on and which unzip can't do, because it doesn't know how many source
73 * arrays there would've been.
74 */
75export function unzip2<T, U>(it: readonly (readonly [T, U])[]) {
76 return (it.length ? (unzip(it) as unknown) : [[], []]) as [T[], U[]];
77}
78
79/**
80 * Util type definitions to allow us to recursively omit a certain key in a type.
81 */
82type OmitDistributive<T, K extends PropertyKey> = T extends any
83 ? T extends object
84 ? Id<OmitRecursively<T, K>>
85 : T
86 : never;
87type Id<T> = {} & { [P in keyof T]: T[P] }; // Cosmetic to make tooltips expand the type
88export type OmitRecursively<T extends any, K extends PropertyKey> = Omit<
89 { [P in keyof T]: OmitDistributive<T[P], K> },
90 K
91>;
92
93export const __throw = (x: unknown): never => {
94 throw x;
95};