Skip to content

Commit 99e35a2

Browse files
Merge: Ensure idempotency so Merge<A, A> returns A (#1336)
Co-authored-by: Som Shekhar Mukherjee <49264891+som-sm@users.noreply.github.com>
1 parent 8f0419c commit 99e35a2

File tree

2 files changed

+19
-4
lines changed

2 files changed

+19
-4
lines changed

source/merge.d.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type {OmitIndexSignature} from './omit-index-signature.d.ts';
22
import type {PickIndexSignature} from './pick-index-signature.d.ts';
33
import type {Simplify} from './simplify.d.ts';
4+
import type {If} from './if.d.ts';
5+
import type {IsEqual} from './is-equal.d.ts';
46

57
// Merges two objects without worrying about index signatures.
68
type SimpleMerge<Destination, Source> = Simplify<{
@@ -47,11 +49,14 @@ Note: If you want a merge type that more accurately reflects the runtime behavio
4749
export type Merge<Destination, Source> =
4850
Destination extends unknown // For distributing `Destination`
4951
? Source extends unknown // For distributing `Source`
50-
? Simplify<
51-
SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>>
52-
& SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>
53-
>
52+
? If<IsEqual<Destination, Source>, Destination, _Merge<Destination, Source>>
5453
: never // Should never happen
5554
: never; // Should never happen
5655

56+
export type _Merge<Destination, Source> =
57+
Simplify<
58+
SimpleMerge<PickIndexSignature<Destination>, PickIndexSignature<Source>>
59+
& SimpleMerge<OmitIndexSignature<Destination>, OmitIndexSignature<Source>>
60+
>;
61+
5762
export {};

test-d/merge.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,13 @@ expectType<
187187
{[x: number]: number; foo: number} | {bar: boolean; baz: boolean}
188188
>,
189189
);
190+
191+
// Idempotency
192+
type TestIntersectionObject = {a: string} & {b: string};
193+
// Note: If `Merge` simplified `TestIntersectionObject` to `{a: string; b: string}` then the following test would fail,
194+
// because `expectType` doesn't consider `{a: string; b: string}` equal to `{a: string} & {b: string}`.
195+
expectType<TestIntersectionObject>({} as Merge<TestIntersectionObject, TestIntersectionObject>);
196+
197+
// Idempotency: Unions
198+
expectType<TestIntersectionObject | {a: string; b: string; c: string}>({} as Merge<TestIntersectionObject | {c: string}, TestIntersectionObject>);
199+
expectType<TestIntersectionObject | {a: string; b: string; c: string}>({} as Merge<TestIntersectionObject, TestIntersectionObject | {c: string}>);

0 commit comments

Comments
 (0)