From c7226f971740966282530745030123aa07ff7b17 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 16 Aug 2022 13:39:45 +0100 Subject: [PATCH] fix: merge object strings of many types (#44) --- src/types.ts | 22 ++++++++++++++-------- test/defu.test.ts | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/types.ts b/src/types.ts index 5b3da44..8666bb9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ type Input = Record -type IgnoredInput = boolean | number | null | any[] | undefined +type IgnoredInput = boolean | number | null | any[] | Record | undefined export type Merger = ( obj: T, @@ -25,16 +25,22 @@ type MergeObjects< : Merge // eslint-disable-line no-use-before-define } -export type DefuFn = ( +type Coalesce> = + D extends [infer F, ...infer Rest] + ? F extends Input + ? Coalesce, Rest> + : F extends IgnoredInput + ? Coalesce + : S + : S + +export type DefuFn = >( source: Source, - ...defaults: Defaults[] -) => MergeObjects + ...defaults: Defaults +) => Coalesce export interface Defu { - (source: Source | IgnoredInput, ...defaults: Array): MergeObjects< - Source, - Defaults - > + >(source: Source | IgnoredInput, ...defaults: Defaults): Coalesce fn: DefuFn arrayFn: DefuFn extend(merger?: Merger): DefuFn diff --git a/test/defu.test.ts b/test/defu.test.ts index 8ae5751..9525610 100644 --- a/test/defu.test.ts +++ b/test/defu.test.ts @@ -75,7 +75,7 @@ describe('defu', () => { b: 2, c: 3 }) - expectTypeOf(result).toEqualTypeOf<{ a: string | number, b: string | number, c?: number }>() + expectTypeOf(result).toEqualTypeOf<{ a: string | number, b: string | number, c: number }>() }) it('should not override Object prototype', () => { @@ -96,6 +96,37 @@ describe('defu', () => { }) }) + it('should merge types of more than two objects', () => { + interface SomeConfig { foo: string } + interface SomeOtherConfig { bar: string[] } + interface ThirdConfig { baz: number[] } + interface ExpectedMergedType { + foo: string + bar: string[] + baz: number[] + } + expectTypeOf(defu({} as SomeConfig, {} as SomeOtherConfig, {} as ThirdConfig)) + .toEqualTypeOf() + }) + + it('should allow partials within merge chain', () => { + interface SomeConfig { foo: string[] } + interface SomeOtherConfig { bar: string[] } + interface ExpectedMergedType { + foo: string[] + bar: string[] + } + let options: (SomeConfig & SomeOtherConfig) | undefined + + expectTypeOf( + defu(options ?? {}, { foo: ['test'] }, { bar: ['test2'] }, {}) + ).toEqualTypeOf() + + expectTypeOf( + defu({ foo: ['test'] }, {}, { bar: ['test2'] }, {}) + ).toEqualTypeOf() + }) + it('custom merger', () => { const ext = createDefu((obj, key, val) => { if (typeof val === 'number') {