Skip to content

Commit 5af60a1

Browse files
is-equal: Fix handling with intersecting wrapped types (#1231)
Co-authored-by: Som Shekhar Mukherjee <iamssmkhrj@gmail.com>
1 parent a318906 commit 5af60a1

File tree

2 files changed

+70
-14
lines changed

2 files changed

+70
-14
lines changed

source/is-equal.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type {IsNever} from './is-never.d.ts';
12
/**
23
Returns a boolean for whether the two given types are equal.
34
@@ -25,6 +26,12 @@ type Includes<Value extends readonly any[], Item> =
2526
@category Utilities
2627
*/
2728
export type IsEqual<A, B> =
29+
[A, B] extends [infer A, infer B]
30+
? _IsEqual<A, B>
31+
: false;
32+
33+
// This version fails the `equalWrappedTupleIntersectionToBeNeverAndNeverExpanded` test in `test-d/is-equal.ts`.
34+
type _IsEqual<A, B> =
2835
(<G>() => G extends A & G | G ? 1 : 2) extends
2936
(<G>() => G extends B & G | G ? 1 : 2)
3037
? true

test-d/is-equal.ts

Lines changed: 63 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,51 @@
11
import {expectType} from 'tsd';
22
import type {IsEqual} from '../index.d.ts';
3+
import type {BuildTuple} from '../source/internal/index.d.ts';
34

4-
const notEqualNumberAndString: IsEqual<number, string> = false;
5-
expectType<false>(notEqualNumberAndString);
5+
expectType<false>({} as IsEqual<number, string>);
6+
expectType<true>({} as IsEqual<1, 1>);
7+
expectType<false>({} as IsEqual<'A', 'B'>);
8+
expectType<true>({} as IsEqual<'foo', 'foo'>);
9+
expectType<false>({} as IsEqual<true, false>);
10+
expectType<true>({} as IsEqual<false, false>);
611

7-
const equalNumbers: IsEqual<1, 1> = true;
8-
expectType<true>(equalNumbers);
12+
expectType<false>({} as IsEqual<any, number>);
13+
expectType<false>({} as IsEqual<'', never>);
14+
expectType<true>({} as IsEqual<any, any>);
15+
expectType<true>({} as IsEqual<never, never>);
16+
expectType<false>({} as IsEqual<any, never>);
17+
expectType<false>({} as IsEqual<never, any>);
18+
expectType<false>({} as IsEqual<any, unknown>);
19+
expectType<false>({} as IsEqual<[any], [never]>);
20+
expectType<true>({} as IsEqual<[any], [any]>);
21+
expectType<true>({} as IsEqual<[never], [never]>);
922

10-
const notEqualAnyAndNumber: IsEqual<any, number> = false;
11-
expectType<false>(notEqualAnyAndNumber);
23+
expectType<false>({} as IsEqual<1 | 2, 1>);
24+
expectType<false>({} as IsEqual<1 | 2, 2 | 3>);
25+
expectType<true>({} as IsEqual<1 | 2, 2 | 1>);
26+
expectType<false>({} as IsEqual<boolean, true>);
1227

13-
const notEqualUnionAndNumber: IsEqual<1 | 2, 1> = false;
14-
expectType<false>(notEqualUnionAndNumber);
28+
expectType<true>({} as IsEqual<{a: 1}, {a: 1}>);
29+
expectType<false>({} as IsEqual<{a: 1}, {a?: 1}>);
30+
expectType<false>({} as IsEqual<{a: 1}, {readonly a: 1}>);
1531

16-
const notEqualAnyAndNever: IsEqual<any, never> = false;
17-
expectType<false>(notEqualAnyAndNever);
32+
expectType<true>({} as IsEqual<[], []>);
33+
expectType<true>({} as IsEqual<readonly [], readonly []>);
34+
expectType<false>({} as IsEqual<readonly [], []>);
35+
expectType<true>({} as IsEqual<number[], number[]>);
36+
expectType<true>({} as IsEqual<readonly number[], readonly number[]>);
37+
expectType<false>({} as IsEqual<readonly number[], number[]>);
38+
expectType<true>({} as IsEqual<[string], [string]>);
39+
expectType<false>({} as IsEqual<[string], [string, number]>);
40+
expectType<false>({} as IsEqual<[0, 1] | [0, 2], [0, 2]>);
1841

19-
const notEqualArrayOfAnyAndArrayOfNever: IsEqual<[any], [never]> = false;
20-
expectType<false>(notEqualArrayOfAnyAndArrayOfNever);
42+
type LongTupleNumber = BuildTuple<50, 0>;
43+
expectType<true>({} as IsEqual<LongTupleNumber, LongTupleNumber>);
44+
45+
type ReadonlyLongTupleNumber = Readonly<BuildTuple<50, 0>>;
46+
expectType<true>({} as IsEqual<ReadonlyLongTupleNumber, ReadonlyLongTupleNumber>);
47+
48+
expectType<false>({} as IsEqual<ReadonlyLongTupleNumber, LongTupleNumber>);
2149

2250
// Missing all generic parameters.
2351
// @ts-expect-error
@@ -28,8 +56,29 @@ type A = IsEqual;
2856
type B = IsEqual<number>;
2957

3058
// Test for issue https://github.com/sindresorhus/type-fest/issues/537
31-
type UnionType = IsEqual<{a: 1} & {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
59+
type UnionType = IsEqual<{a: 1} | {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
3260
expectType<UnionType>(true);
3361

34-
type IntersectionType = IsEqual<{a: 1} | {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
62+
type IntersectionType = IsEqual<{a: 1} & {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
3563
expectType<IntersectionType>(true);
64+
65+
// Test for PR https://github.com/sindresorhus/type-fest/pull/1231
66+
type BranchOnWrappedTupleMatches<Tpl> = (Tpl extends [[0, 2]] ? 'Foo' : 'Bar');
67+
type BranchOnWrappedTupleDoesNotMatch<Tpl> = (Tpl extends [[0, 1]] ? 'Foo' : 'Bar');
68+
type BranchOnTupleMatches<Tpl> = (Tpl extends [0, 2] ? 'Foo' : 'Bar');
69+
type BranchOnTupleDoesNotMatch<Tpl> = (Tpl extends [0, 1] ? 'Foo' : 'Bar');
70+
71+
declare const equalWrappedTupleIntersectionToBeNeverAndNever: IsEqual<(BranchOnWrappedTupleMatches<[[0, 2]]> & BranchOnWrappedTupleDoesNotMatch<[[0, 2]]>), never>;
72+
expectType<true>(equalWrappedTupleIntersectionToBeNeverAndNever);
73+
74+
declare const equalWrappedTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? IsEqual<(BranchOnWrappedTupleMatches<[Tpl]> & BranchOnWrappedTupleDoesNotMatch<[Tpl]>), never> : never;
75+
expectType<true>(equalWrappedTupleIntersectionToBeNeverAndNeverExpanded);
76+
77+
declare const equalTupleIntersectionToBeNeverAndNever: IsEqual<(BranchOnTupleMatches<[0, 2]> & BranchOnTupleDoesNotMatch<[0, 2]>), never>;
78+
expectType<true>(equalTupleIntersectionToBeNeverAndNever);
79+
80+
declare const equalTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? IsEqual<(BranchOnTupleMatches<Tpl> & BranchOnTupleDoesNotMatch<Tpl>), never> : never;
81+
expectType<true>(equalTupleIntersectionToBeNeverAndNeverExpanded);
82+
83+
declare const equalTupleIntersectionAndTuple: IsEqual<[{a: 1}] & [{a: 1}], [{a: 1}]>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
84+
expectType<true>(equalTupleIntersectionAndTuple);

0 commit comments

Comments
 (0)