import { IsUnknown, ValueOf } from 'type-fest'; import { TuplifyUnion } from './tuplify-union'; type TupleOrArray = readonly [...unknown[]]; /** * An object or an array. */ export type Spreadable = object | TupleOrArray; type InnerDeepKeysOfSpreadable< Obj extends Record, Keys extends string | number | symbol = keyof Obj, > = ValueOf<{ [K in Keys]: Obj[K] extends Spreadable ? InnerDeepKeysOfSpreadable : K; }>; /** * Get all keys of an spreadable type recursively. * @example * ```ts * type Obj = { * 0: string; * a: { * b: { * c: string; * d: string; * }; * }; * e: string; * f: string; * [Symbol.hasInstance]: 0; * }; * type Keys = DeepKeysOfObjectFormula; // `Keys` equivalent to 0 | 'c' | 'd' | 'f' | Symbol.hasInstance; */ export type DeepKeysOfSpreadable> = InnerDeepKeysOfSpreadable; /** * Merge multiple objects * @example * ```ts * type O1 = { * a: 'a'; * c: 'c'; * d: { * d1: 0; * }; * e: 'e'; * f: 'f1'; * }; * type O2 = { * b: 'b'; * c: 'c'; * d: { * d2: 1; * }; * e: { e: 'e'; }; * f: 'f2'; * }; * type Res = MergeObjects<[O1, O2, ...]>; // { a?: 'a'; b?: 'b'; c: 'c'; d: { d1?: 0; d2?: 1; }; e: 'e' | { e: 'e'; }; f: 'f1' | 'f2'; }; * ``` */ export type MergeObjects = Objects extends [infer O1, infer O2, ...infer Others] ? Others extends [] ? MergeTwoObjects // @ts-ignore : MergeTwoObjects> : never; type MergeTwoObjects = Omit<{ [K in (keyof O1 | keyof O2)]?: // @ts-ignore IsUnknown extends true // @ts-ignore ? O2[K] // @ts-ignore : IsUnknown extends true // @ts-ignore ? O1[K] : (O1 & O2)[K]; }, keyof (O1 | O2)> & { [K in keyof (O1 | O2)]: MergeValue; }; type IsExtendObject = T extends object ? true : false; type MergeValue< K extends string | number | symbol, O1, O2, // @ts-ignore V1 = O1[K], // @ts-ignore V2 = O2[K], > = IsUnknown extends true ? V2 : IsUnknown extends true ? unknown : ( IsExtendObject | IsExtendObject extends true ? MergeTwoObjects : V1 | V2 ); /** * Merge multiple objects * @example * ```ts * type O1 = { * a: 'a'; * c: 'c'; * d: { * d1: 0; * }; * e: 'e'; * f: 'f1'; * }; * type O2 = { * b: 'b'; * c: 'c'; * d: { * d2: 1; * }; * e: { e: 'e'; }; * f: 'f2'; * }; * type Res = MixedTuple; // { a?: 'a'; b?: 'b'; c: 'c'; d: { d1?: 0; d2?: 1; }; e: 'e' | { e: 'e'; }; f: 'f1' | 'f2'; }; * ``` */ // @ts-ignore export type MixedTuple = MergeObjects<[...TuplifyUnion, ...TuplifyUnion]>;