1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-06 11:43:51 +00:00

Remove dependenc on MergeDeep

This caused failures when building outside of the IDE for unknown reasons.
This commit is contained in:
Matt Gibson
2025-07-09 08:10:49 -07:00
parent 3b5bf2abf0
commit 14c73a7cfc
2 changed files with 33 additions and 140 deletions

View File

@@ -9,6 +9,10 @@ type ExampleOptions = {
readonly d?: number;
readonly e: string;
};
readonly f: {
readonly h?: number;
readonly i: string;
};
};
const EXAMPLE_DEFAULTS = Object.freeze({
@@ -27,12 +31,16 @@ describe("mergeOptions", () => {
const options: ExampleOptions = {
required_func: () => {},
b: "test",
f: {
i: "example",
},
};
const merged = mergeOptions(options, EXAMPLE_DEFAULTS);
// can access properties
expect(merged.a).toBe(42);
expect(merged.c.d).toBe(1);
expect(merged).toEqual({
a: 0,

View File

@@ -1,151 +1,36 @@
/* eslint-disable @typescript-eslint/no-empty-object-type -- used in type-fest's code*/
import { RequiredKeysOf, Simplify, Primitive } from "type-fest";
import { Simplify, Primitive, ConditionalKeys } from "type-fest";
type Function = (...args: any[]) => any;
/** FIXME: this is pulled from type-fest-v4. remove when we update package */
export type ConditionalSimplifyDeep<
Type,
ExcludeType = never,
IncludeType = unknown,
> = Type extends ExcludeType
? Type
: Type extends IncludeType
? { [TypeKey in keyof Type]: ConditionalSimplifyDeep<Type[TypeKey], ExcludeType, IncludeType> }
: Type;
export type BuiltIns = Primitive | void | Date | RegExp;
export type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unknown);
export type SimplifyDeep<Type, ExcludeType = never> = ConditionalSimplifyDeep<
Type,
ExcludeType | NonRecursiveType | Set<unknown> | Map<unknown, unknown>,
object
>;
/** FIXME: taken from type-fest-v4. remove once we upgrade */
export type RequiredKeysOf<BaseType extends object> = BaseType extends unknown // For distributing `BaseType`
? Exclude<keyof BaseType, OptionalKeysOf<BaseType>>
: never; // Should never happen
export type OptionalKeysOf<BaseType extends object> = BaseType extends unknown // For distributing `BaseType`
? keyof {
[Key in keyof BaseType as BaseType extends Record<Key, BaseType[Key]> ? never : Key]: never;
} &
keyof BaseType // Intersect with `keyof BaseType` to ensure result of `OptionalKeysOf<BaseType>` is always assignable to `keyof BaseType`
: never; // Should never happen
type SimplifyDeepExcludeArray<T> = SimplifyDeep<T, UnknownArray>;
export type UnknownArray = readonly unknown[];
export type UnknownRecord = Record<PropertyKey, unknown>;
export type UnknownArrayOrTuple = readonly [...unknown[]];
export type OmitIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
? never
: KeyType]: ObjectType[KeyType];
};
export type PickIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
? KeyType
: never]: ObjectType[KeyType];
};
/** END FIXME: taken from type-fest-v4. remove once we upgrade */
type MergeDeepRecordProperty<Destination, Source> = undefined extends Source
?
| MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>>
| undefined
: MergeDeepOrReturn<Source, Destination, Source>;
type RequiredFilter<Type, Key extends keyof Type> = undefined extends Type[Key]
? Type[Key] extends undefined
? Key
: never
: Key;
// Returns `never` if the key is required otherwise return the key type.
type OptionalFilter<Type, Key extends keyof Type> = undefined extends Type[Key]
? Type[Key] extends undefined
? never
: Key
: never;
export type EnforceOptional<ObjectType> = Simplify<
// Merges options with defaults, deeply preferring default types over options types.
// FIXME: It's likely the type-fests `MergeDeep` will be a better fit for this, but that is in v4+,
// marked experimental, _and_ caused flaky build failures when testing.
export type OptionsWithDefaultsDeep<Options, Defaults> = Simplify<
{
[Key in keyof ObjectType as RequiredFilter<ObjectType, Key>]: ObjectType[Key];
[K in keyof Required<Options> & keyof Defaults]: Defaults[K] extends Primitive | Function
? Defaults[K]
: Defaults[K] extends object
? // note: exclude undefined here so that if the type is a union with undefined, we can still get the keys correctly
OptionsWithDefaultsDeep<Exclude<Options[K], undefined>, Defaults[K]>
: never; // should only ever be primitive, function, or object
} & {
[Key in keyof ObjectType as OptionalFilter<ObjectType, Key>]?: Exclude<
ObjectType[Key],
undefined
>;
[K in Exclude<keyof Required<Options>, keyof Defaults>]: Options[K];
}
>;
type MergeDeepOrReturn<DefaultType, Destination, Source> = SimplifyDeepExcludeArray<
[undefined] extends [Destination | Source]
? DefaultType
: Destination extends UnknownRecord
? Source extends UnknownRecord
? MergeDeepRecord<Destination, Source>
: DefaultType
: Destination extends UnknownArrayOrTuple
? Source extends UnknownArrayOrTuple
? MergeDeepArrayOrTuple<Destination, Source>
: DefaultType
: DefaultType
>;
type MergeDeepArrayOrTuple<
Destination extends UnknownArrayOrTuple,
Source extends UnknownArrayOrTuple,
> = Array<Exclude<Destination, undefined>[number] | Exclude<Source, undefined>[number]>;
type MergeDeepRecord<
Destination extends UnknownRecord,
Source extends UnknownRecord,
> = DoMergeDeepRecord<OmitIndexSignature<Destination>, OmitIndexSignature<Source>> &
Merge<PickIndexSignature<Destination>, PickIndexSignature<Source>>;
type DoMergeDeepRecord<Destination extends UnknownRecord, Source extends UnknownRecord> =
// Case in rule 1: The destination contains the key but the source doesn't.
{
[Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key];
} & {
// Case in rule 2: The source contains the key but the destination doesn't.
[Key in keyof Source as Key extends keyof Destination ? never : Key]: Source[Key];
} & {
// Case in rule 3: Both the source and the destination contain the key.
[Key in keyof Source as Key extends keyof Destination ? Key : never]: MergeDeepRecordProperty<
Destination[Key],
Source[Key]
>;
};
type SimpleMerge<Destination, Source> = {
[Key in keyof Destination as Key extends keyof Source ? never : Key]: Destination[Key];
} & Source;
export type Merge<Destination, Source> = Simplify<
SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>> &
SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>
>;
export type IfNever<T, TypeIfNever = true, TypeIfNotNever = false> =
IsNever<T> extends true ? TypeIfNever : TypeIfNotNever;
export type IsNever<T> = [T] extends [never] ? true : false;
type MergeDeep<Destination, Source> = SimplifyDeepExcludeArray<
[undefined] extends [Destination | Source]
? never
: Destination extends UnknownRecord
? Source extends UnknownRecord
? MergeDeepRecord<Destination, Source>
: never
: Destination extends UnknownArrayOrTuple
? Source extends UnknownArrayOrTuple
? MergeDeepArrayOrTuple<Destination, Source>
: never
: never
>;
export type ConditionalKeys<Base, Condition> = {
// Map through all the keys of the given base type.
[Key in keyof Base]-?: Base[Key] extends Condition // Pick only keys with types extending the given `Condition` type.
? // Retain this key
// If the value for the key extends never, only include it if `Condition` also extends never
IfNever<Base[Key], IfNever<Condition, Key, never>, Key>
: // Discard this key since the condition fails.
never;
// Convert the produced object into a union type of the keys which passed the conditional test.
}[keyof Base];
/** END FIXME: this is pulled from type-fest-v4. remove when we update package */
export type OptionsWithDefaultsDeep<Options, Defaults> =
// FIXME: replace with MergeDeep<Options, Defaults> when type-fest is updated to v4+
MergeDeep<Options, Defaults>;
type DefaultsForDeep<Options extends object> = Simplify<
Omit<
Required<{
@@ -171,10 +56,10 @@ type RequiredMethodKeysOf<Obj extends object> = RequiredKeysOf<
export function mergeOptions<
Options extends object,
const Defaults extends DefaultsForDeep<Options>,
// const Defaults extends DefaultsForDeep<Options>,
>(
options: Options,
defaults: Defaults,
defaults: DefaultsForDeep<Options>, //Defaults,
): OptionsWithDefaultsDeep<Options, DefaultsForDeep<Options>> {
const result = { ...options } as any;
for (const key in defaults) {