Skip to content

Commit

Permalink
fix: merge object strings of many types (#44)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Aug 16, 2022
1 parent 8ac7409 commit c7226f9
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 9 deletions.
22 changes: 14 additions & 8 deletions src/types.ts
@@ -1,5 +1,5 @@
type Input = Record<string | number | symbol, any>
type IgnoredInput = boolean | number | null | any[] | undefined
type IgnoredInput = boolean | number | null | any[] | Record<never, any> | undefined

export type Merger = <T extends Input, K extends keyof T>(
obj: T,
Expand All @@ -25,16 +25,22 @@ type MergeObjects<
: Merge<Destination[Key], Defaults[Key]> // eslint-disable-line no-use-before-define
}

export type DefuFn = <Source extends Input, Defaults extends Input>(
type Coalesce<S extends Input, D extends Array<Input | IgnoredInput>> =
D extends [infer F, ...infer Rest]
? F extends Input
? Coalesce<MergeObjects<S, F>, Rest>
: F extends IgnoredInput
? Coalesce<S, Rest>
: S
: S

export type DefuFn = <Source extends Input, Defaults extends Array<Input | IgnoredInput>>(
source: Source,
...defaults: Defaults[]
) => MergeObjects<Source, Defaults>
...defaults: Defaults
) => Coalesce<Source, Defaults>

export interface Defu {
<Source extends Input, Defaults extends Input>(source: Source | IgnoredInput, ...defaults: Array<Defaults | IgnoredInput>): MergeObjects<
Source,
Defaults
>
<Source extends Input, Defaults extends Array<Input | IgnoredInput>>(source: Source | IgnoredInput, ...defaults: Defaults): Coalesce<Source, Defaults>
fn: DefuFn
arrayFn: DefuFn
extend(merger?: Merger): DefuFn
Expand Down
33 changes: 32 additions & 1 deletion test/defu.test.ts
Expand Up @@ -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', () => {
Expand All @@ -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<ExpectedMergedType>()
})

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<ExpectedMergedType>()

expectTypeOf(
defu({ foo: ['test'] }, {}, { bar: ['test2'] }, {})
).toEqualTypeOf<ExpectedMergedType>()
})

it('custom merger', () => {
const ext = createDefu((obj, key, val) => {
if (typeof val === 'number') {
Expand Down

0 comments on commit c7226f9

Please sign in to comment.