alacritty-theme-switch
Version:
CLI utility for switching Alacritty color themes
322 lines • 11.1 kB
TypeScript
/**
* Merges the two given records, recursively merging any nested records with the
* second collection overriding the first in case of conflict.
*
* For arrays, maps and sets, a merging strategy can be specified to either
* `replace` values, or `merge` them instead.
*
* @typeParam T Type of the first record
*
* @param record First record to merge.
* @param other Second record to merge.
* @param options Merging options.
*
* @returns A new record with the merged values.
*
* @example Merge objects
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: true };
* const b = { foo: { bar: true } };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: { bar: true } };
*
* assertEquals(result, expected);
* ```
*
* @example Merge arrays
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: [1, 2] };
* const b = { foo: [3, 4] };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: [1, 2, 3, 4] };
*
* assertEquals(result, expected);
* ```
*
* @example Merge maps
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: new Map([["a", 1]]) };
* const b = { foo: new Map([["b", 2]]) };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: new Map([["a", 1], ["b", 2]]) };
*
* assertEquals(result, expected);
* ```
*
* @example Merge sets
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: new Set([1]) };
* const b = { foo: new Set([2]) };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: new Set([1, 2]) };
*
* assertEquals(result, expected);
* ```
*
* @example Merge with custom options
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: [1, 2] };
* const b = { foo: [3, 4] };
*
* const result = deepMerge(a, b, { arrays: "replace" });
*
* const expected = { foo: [3, 4] };
*
* assertEquals(result, expected);
* ```
*/
export declare function deepMerge<T extends Record<PropertyKey, unknown>>(record: Partial<Readonly<T>>, other: Partial<Readonly<T>>, options?: Readonly<DeepMergeOptions>): T;
/**
* Merges the two given records, recursively merging any nested records with the
* second collection overriding the first in case of conflict.
*
* For arrays, maps and sets, a merging strategy can be specified to either
* `replace` values, or `merge` them instead.
*
* @typeParam T Type of the first record
* @typeParam U Type of the second record
* @typeParam Options Merging options
*
* @param record First record to merge.
* @param other Second record to merge.
* @param options Merging options.
*
* @returns A new record with the merged values.
*
* @example Merge objects
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: true };
* const b = { foo: { bar: true } };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: { bar: true } };
*
* assertEquals(result, expected);
* ```
*
* @example Merge arrays
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: [1, 2] };
* const b = { foo: [3, 4] };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: [1, 2, 3, 4] };
*
* assertEquals(result, expected);
* ```
*
* @example Merge maps
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: new Map([["a", 1]]) };
* const b = { foo: new Map([["b", 2]]) };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: new Map([["a", 1], ["b", 2]]) };
*
* assertEquals(result, expected);
* ```
*
* @example Merge sets
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: new Set([1]) };
* const b = { foo: new Set([2]) };
*
* const result = deepMerge(a, b);
*
* const expected = { foo: new Set([1, 2]) };
*
* assertEquals(result, expected);
* ```
*
* @example Merge with custom options
* ```ts
* import { deepMerge } from "@std/collections/deep-merge";
* import { assertEquals } from "@std/assert";
*
* const a = { foo: [1, 2] };
* const b = { foo: [3, 4] };
*
* const result = deepMerge(a, b, { arrays: "replace" });
*
* const expected = { foo: [3, 4] };
*
* assertEquals(result, expected);
* ```
*/
export declare function deepMerge<T extends Record<PropertyKey, unknown>, U extends Record<PropertyKey, unknown>, Options extends DeepMergeOptions>(record: Readonly<T>, other: Readonly<U>, options?: Readonly<Options>): DeepMerge<T, U, Options>;
/** Merging strategy */
export type MergingStrategy = "replace" | "merge";
/** Options for {@linkcode deepMerge}. */
export type DeepMergeOptions = {
/**
* Merging strategy for arrays
*
* @default {"merge"}
*/
arrays?: MergingStrategy;
/**
* Merging strategy for maps.
*
* @default {"merge"}
*/
maps?: MergingStrategy;
/**
* Merging strategy for sets.
*
* @default {"merge"}
*/
sets?: MergingStrategy;
};
/**
* How does recursive typing works ?
*
* Deep merging process is handled through `DeepMerge<T, U, Options>` type.
* If both T and U are Records, we recursively merge them,
* else we treat them as primitives.
*
* Merging process is handled through `Merge<T, U>` type, in which
* we remove all maps, sets, arrays and records so we can handle them
* separately depending on merging strategy:
*
* Merge<
* {foo: string},
* {bar: string, baz: Set<unknown>},
* > // "foo" and "bar" will be handled with `MergeRightOmitComplexes`
* // "baz" will be handled with `MergeAll*` type
*
* `MergeRightOmitComplexes<T, U>` will do the above: all T's
* exclusive keys will be kept, though common ones with U will have their
* typing overridden instead:
*
* MergeRightOmitComplexes<
* {foo: string, baz: number},
* {foo: boolean, bar: string}
* > // {baz: number, foo: boolean, bar: string}
* // "baz" was kept from T
* // "foo" was overridden by U's typing
* // "bar" was added from U
*
* For Maps, Arrays, Sets and Records, we use `MergeAll*<T, U>` utility
* types. They will extract relevant data structure from both T and U
* (providing that both have same data data structure, except for typing).
*
* From these, `*ValueType<T>` will extract values (and keys) types to be
* able to create a new data structure with an union typing from both
* data structure of T and U:
*
* MergeAllSets<
* {foo: Set<number>},
* {foo: Set<string>}
* > // `SetValueType` will extract "number" for T
* // `SetValueType` will extract "string" for U
* // `MergeAllSets` will infer type as Set<number|string>
* // Process is similar for Maps, Arrays, and Sets
*
* `DeepMerge<T, U, Options>` is taking a third argument to be handle to
* infer final typing depending on merging strategy:
*
* & (Options extends { sets: "replace" } ? PartialByType<U, Set<unknown>>
* : MergeAllSets<T, U>)
*
* In the above line, if "Options" have its merging strategy for Sets set to
* "replace", instead of performing merging of Sets type, it will take the
* typing from right operand (U) instead, effectively replacing the typing.
*
* An additional note, we use `ExpandRecursively<T>` utility type to expand
* the resulting typing and hide all the typing logic of deep merging so it is
* more user friendly.
*/
/** Force intellisense to expand the typing to hide merging typings */
export type ExpandRecursively<T> = T extends Record<PropertyKey, unknown> ? T extends infer O ? {
[K in keyof O]: ExpandRecursively<O[K]>;
} : never : T;
/** Filter of keys matching a given type */
export type PartialByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
/** Get set values type */
export type SetValueType<T> = T extends Set<infer V> ? V : never;
/** Merge all sets types definitions from keys present in both objects */
export type MergeAllSets<T, U, X = PartialByType<T, Set<unknown>>, Y = PartialByType<U, Set<unknown>>, Z = {
[K in keyof X & keyof Y]: Set<SetValueType<X[K]> | SetValueType<Y[K]>>;
}> = Z;
/** Get array values type */
export type ArrayValueType<T> = T extends Array<infer V> ? V : never;
/** Merge all sets types definitions from keys present in both objects */
export type MergeAllArrays<T, U, X = PartialByType<T, Array<unknown>>, Y = PartialByType<U, Array<unknown>>, Z = {
[K in keyof X & keyof Y]: Array<ArrayValueType<X[K]> | ArrayValueType<Y[K]>>;
}> = Z;
/** Get map values types */
export type MapKeyType<T> = T extends Map<infer K, unknown> ? K : never;
/** Get map values types */
export type MapValueType<T> = T extends Map<unknown, infer V> ? V : never;
/** Merge all sets types definitions from keys present in both objects */
export type MergeAllMaps<T, U, X = PartialByType<T, Map<unknown, unknown>>, Y = PartialByType<U, Map<unknown, unknown>>, Z = {
[K in keyof X & keyof Y]: Map<MapKeyType<X[K]> | MapKeyType<Y[K]>, MapValueType<X[K]> | MapValueType<Y[K]>>;
}> = Z;
/** Merge all records types definitions from keys present in both objects */
export type MergeAllRecords<T, U, Options, X = PartialByType<T, Record<PropertyKey, unknown>>, Y = PartialByType<U, Record<PropertyKey, unknown>>, Z = {
[K in keyof X & keyof Y]: DeepMerge<X[K], Y[K], Options>;
}> = Z;
/** Exclude map, sets and array from type */
export type OmitComplexes<T> = Omit<T, keyof PartialByType<T, Map<unknown, unknown> | Set<unknown> | Array<unknown> | Record<PropertyKey, unknown>>>;
/** Object with keys in either T or U but not in both */
export type ObjectXorKeys<T, U, X = Omit<T, keyof U> & Omit<U, keyof T>, Y = {
[K in keyof X]: X[K];
}> = Y;
/** Merge two objects, with left precedence */
export type MergeRightOmitComplexes<T, U, X = ObjectXorKeys<T, U> & OmitComplexes<{
[K in keyof U]: U[K];
}>> = X;
/** Merge two objects */
export type Merge<T, U, Options, X = MergeRightOmitComplexes<T, U> & MergeAllRecords<T, U, Options> & (Options extends {
sets: "replace";
} ? PartialByType<U, Set<unknown>> : MergeAllSets<T, U>) & (Options extends {
arrays: "replace";
} ? PartialByType<U, Array<unknown>> : MergeAllArrays<T, U>) & (Options extends {
maps: "replace";
} ? PartialByType<U, Map<unknown, unknown>> : MergeAllMaps<T, U>)> = ExpandRecursively<X>;
/** Merge deeply two objects */
export type DeepMerge<T, U, Options = Record<string, MergingStrategy>> = [
T,
U
] extends [Record<PropertyKey, unknown>, Record<PropertyKey, unknown>] ? Merge<T, U, Options> : T | U;
//# sourceMappingURL=deep_merge.d.ts.map