Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions source/merge-deep.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ type MergeDeepRecordProperty<
Source,
Options extends MergeDeepInternalOptions,
> = undefined extends Source
? MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>, Options> | undefined
? MergeDeepOrReturn<Source, Exclude<Destination, undefined>, Exclude<Source, undefined>, Options> | (undefined extends Destination ? undefined : never)
: MergeDeepOrReturn<Source, Destination, Source, Options>;

/**
Expand All @@ -58,7 +58,7 @@ type DoMergeDeepRecord<
}
// 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], Options>;
[Key in keyof Source as Key extends keyof Destination ? Key : never]: MergeDeepRecordProperty<Required<Destination>[Key], Required<Source>[Key], Options>;
};

/**
Expand Down
40 changes: 36 additions & 4 deletions test-d/merge-deep.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectType} from 'tsd';
import type {MergeDeep, MergeDeepOptions} from '../index.d.ts';
import type {MergeDeep, MergeDeepOptions, IsEqual} from '../index.d.ts';

// Test helper.
declare function mergeDeep<
Expand Down Expand Up @@ -147,8 +147,8 @@ expectType<{
type FooWithOptional = {foo: string; fooOptional?: string; fooBar: Foo; fooBarOptional: Foo | undefined};
type BarWithOptional = {bar: number; barOptional?: number; fooBar: Bar; fooBarOptional: Bar | undefined};

declare const fooBarWithOptional: MergeDeep<FooWithOptional, BarWithOptional>;
expectType<{
type fooBarWithOptional = MergeDeep<FooWithOptional, BarWithOptional>;
expectType<true>({} as IsEqual<fooBarWithOptional, {
foo: string;
bar: number;
fooOptional?: string;
Expand All @@ -165,7 +165,7 @@ expectType<{
fooBar: boolean;
items: number[];
} | undefined;
}>(fooBarWithOptional);
}>);

// Test for optional
type FooOptional = {
Expand Down Expand Up @@ -309,3 +309,35 @@ expectType<[Bar, FooBarReplace, ...FooBarReplace[]]>(tupleIntoTupleWithVariadicR

declare const tupleIntoTupleWithVariadicReplaceReversed: MergeDeep<[Foo, ...Foo[]], [number, Bar, ...Bar[]], {arrayMergeMode: 'replace'; recurseIntoArrays: true}>;
expectType<[number, FooBarReplace, ...FooBarReplace[]]>(tupleIntoTupleWithVariadicReplaceReversed);

// Test for https://github.com/sindresorhus/type-fest/issues/1314
type RecordNotPartial = {
name: string;
type: string;
b: {bb?: string; cc: string};
};
type RecordPartial = Partial<RecordNotPartial>;

type testOptionalRecordReplacedByRecord = MergeDeep<RecordPartial, RecordNotPartial>;
expectType<true>({} as IsEqual<RecordNotPartial, testOptionalRecordReplacedByRecord>);

type testRecordReplacedByOptionalRecord = MergeDeep<RecordNotPartial, RecordPartial>;
expectType<true>({} as IsEqual<RecordPartial, testRecordReplacedByOptionalRecord>);

type NodeOptional = {
name: string;
type: string;
b: {bb?: string; cc: string};
};

type NodeNotOptional = {
name: string;
type: string;
b: {bb: string; cc: string | undefined};
};

type testExpectedNotOptional = MergeDeep<NodeOptional, NodeNotOptional>;
expectType<true>({} as IsEqual<NodeNotOptional, testExpectedNotOptional>);

type testExpectedOptional = MergeDeep<NodeNotOptional, NodeOptional>;
expectType<true>({} as IsEqual<NodeOptional, testExpectedOptional>);