Skip to content

Commit 0699dc0

Browse files
update: move SimpleIsEqual to internal
1 parent 4baac7e commit 0699dc0

File tree

4 files changed

+123
-16
lines changed

4 files changed

+123
-16
lines changed

source/exclude-exactly.d.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type {IsNever} from './is-never.d.ts';
22
import type {IsAny} from './is-any.d.ts';
33
import type {If} from './if.d.ts';
4-
import type {IfNotAnyOrNever} from './internal/type.d.ts';
4+
import type {IfNotAnyOrNever, SimpleIsEqual} from './internal/type.d.ts';
55

66
/**
77
A stricter version of `Exclude<T, U>` that ensures objects with different key modifiers are not considered identical.
@@ -66,10 +66,4 @@ type _ExcludeExactly<Union, Delete> =
6666
Union, Union
6767
>;
6868

69-
type SimpleIsEqual<A, B> =
70-
(<G>() => G extends A & G | G ? 1 : 2) extends
71-
(<G>() => G extends B & G | G ? 1 : 2)
72-
? true
73-
: false;
74-
7569
export {};

source/internal/type.d.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,28 @@ export type IsExactOptionalPropertyTypesEnabled = [(string | undefined)?] extend
161161
? false
162162
: true;
163163

164+
/**
165+
A simple version of `IsEqual`.
166+
167+
`SimpleIsEqual<never, unknown>` and `SimpleIsEqual<unknown, never>` return `true`, whereas `IsEqual` returns `false` correctly.
168+
169+
`SimpleIsEqual` doesn't return `false` correctly for identical union/intersection arguments.
170+
171+
@example
172+
```
173+
type UnionCase = SimpleIsEqual<{a: {b: 0} | {b: 0}}, {a: {b: 0}}>;
174+
//=> false
175+
176+
type IntersectionCase = SimpleIsEqual<{a: {b: 0} & {b: 0}}, {a: {b: 0}}>;
177+
//=> false
178+
```
179+
180+
`SimpleIsEqual` fails the `equalWrappedTupleIntersectionToBeNeverAndNeverExpanded` test in `test-d/internal/simple-is-equal.ts`.
181+
*/
182+
export type SimpleIsEqual<A, B> =
183+
(<G>() => G extends A & G | G ? 1 : 2) extends
184+
(<G>() => G extends B & G | G ? 1 : 2)
185+
? true
186+
: false;
187+
164188
export {};

source/is-equal.d.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type {IsNever} from './is-never.d.ts';
1+
import type {SimpleIsEqual} from './internal/type.d.ts';
2+
23
/**
34
Returns a boolean for whether the two given types are equal.
45
@@ -28,15 +29,8 @@ type Includes<Value extends readonly any[], Item> =
2829
export type IsEqual<A, B> =
2930
[A] extends [B]
3031
? [B] extends [A]
31-
? _IsEqual<A, B>
32+
? SimpleIsEqual<A, B>
3233
: false
3334
: false;
3435

35-
// This version fails the `equalWrappedTupleIntersectionToBeNeverAndNeverExpanded` test in `test-d/is-equal.ts`.
36-
type _IsEqual<A, B> =
37-
(<G>() => G extends A & G | G ? 1 : 2) extends
38-
(<G>() => G extends B & G | G ? 1 : 2)
39-
? true
40-
: false;
41-
4236
export {};

test-d/internal/simple-is-equal.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {expectType} from 'tsd';
2+
import type {TupleOf} from '../../index.d.ts';
3+
import type {SimpleIsEqual} from '../../source/internal/index.d.ts';
4+
5+
expectType<false>({} as SimpleIsEqual<number, string>);
6+
expectType<true>({} as SimpleIsEqual<1, 1>);
7+
expectType<false>({} as SimpleIsEqual<'A', 'B'>);
8+
expectType<true>({} as SimpleIsEqual<'foo', 'foo'>);
9+
expectType<false>({} as SimpleIsEqual<true, false>);
10+
expectType<true>({} as SimpleIsEqual<false, false>);
11+
12+
expectType<false>({} as SimpleIsEqual<any, number>);
13+
expectType<false>({} as SimpleIsEqual<'', never>);
14+
expectType<true>({} as SimpleIsEqual<any, any>);
15+
expectType<true>({} as SimpleIsEqual<never, never>);
16+
expectType<false>({} as SimpleIsEqual<any, never>);
17+
expectType<false>({} as SimpleIsEqual<never, any>);
18+
expectType<false>({} as SimpleIsEqual<any, unknown>);
19+
// `IsEqual` returns `false`, `SimpleIsEqual` returns `true`.
20+
expectType<true>({} as SimpleIsEqual<never, unknown>);
21+
// `IsEqual` returns `false`, `SimpleIsEqual` returns `true`.
22+
expectType<true>({} as SimpleIsEqual<unknown, never>);
23+
expectType<false>({} as SimpleIsEqual<[never], [unknown]>);
24+
expectType<false>({} as SimpleIsEqual<[unknown], [never]>);
25+
expectType<false>({} as SimpleIsEqual<[any], [never]>);
26+
expectType<true>({} as SimpleIsEqual<[any], [any]>);
27+
expectType<true>({} as SimpleIsEqual<[never], [never]>);
28+
29+
expectType<false>({} as SimpleIsEqual<1 | 2, 1>);
30+
expectType<false>({} as SimpleIsEqual<1 | 2, 2 | 3>);
31+
expectType<true>({} as SimpleIsEqual<1 | 2, 2 | 1>);
32+
expectType<false>({} as SimpleIsEqual<boolean, true>);
33+
34+
expectType<true>({} as SimpleIsEqual<{a: 1}, {a: 1}>);
35+
expectType<false>({} as SimpleIsEqual<{a: 1}, {a?: 1}>);
36+
expectType<false>({} as SimpleIsEqual<{a: 1}, {readonly a: 1}>);
37+
38+
expectType<true>({} as SimpleIsEqual<[], []>);
39+
expectType<true>({} as SimpleIsEqual<readonly [], readonly []>);
40+
expectType<false>({} as SimpleIsEqual<readonly [], []>);
41+
expectType<true>({} as SimpleIsEqual<number[], number[]>);
42+
expectType<true>({} as SimpleIsEqual<readonly number[], readonly number[]>);
43+
expectType<false>({} as SimpleIsEqual<readonly number[], number[]>);
44+
expectType<true>({} as SimpleIsEqual<[string], [string]>);
45+
expectType<false>({} as SimpleIsEqual<[string], [string, number]>);
46+
expectType<false>({} as SimpleIsEqual<[0, 1] | [0, 2], [0, 2]>);
47+
48+
type LongTupleNumber = TupleOf<50, 0>;
49+
expectType<true>({} as SimpleIsEqual<LongTupleNumber, LongTupleNumber>);
50+
51+
type ReadonlyLongTupleNumber = Readonly<TupleOf<50, 0>>;
52+
expectType<true>({} as SimpleIsEqual<ReadonlyLongTupleNumber, ReadonlyLongTupleNumber>);
53+
54+
expectType<false>({} as SimpleIsEqual<ReadonlyLongTupleNumber, LongTupleNumber>);
55+
56+
// Missing all generic parameters.
57+
// @ts-expect-error
58+
type A = SimpleIsEqual;
59+
60+
// Missing `Y` generic parameter.
61+
// @ts-expect-error
62+
type B = SimpleIsEqual<number>;
63+
64+
// Test for issue https://github.com/sindresorhus/type-fest/issues/537
65+
type UnionType = SimpleIsEqual<{a: 1} | {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
66+
expectType<UnionType>(true);
67+
68+
type IntersectionType = SimpleIsEqual<{a: 1} & {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
69+
expectType<IntersectionType>(true);
70+
71+
// Test for PR https://github.com/sindresorhus/type-fest/pull/1231
72+
type BranchOnWrappedTupleMatches<Tpl> = (Tpl extends [[0, 2]] ? 'Foo' : 'Bar');
73+
type BranchOnWrappedTupleDoesNotMatch<Tpl> = (Tpl extends [[0, 1]] ? 'Foo' : 'Bar');
74+
type BranchOnTupleMatches<Tpl> = (Tpl extends [0, 2] ? 'Foo' : 'Bar');
75+
type BranchOnTupleDoesNotMatch<Tpl> = (Tpl extends [0, 1] ? 'Foo' : 'Bar');
76+
77+
declare const equalWrappedTupleIntersectionToBeNeverAndNever: SimpleIsEqual<(BranchOnWrappedTupleMatches<[[0, 2]]> & BranchOnWrappedTupleDoesNotMatch<[[0, 2]]>), never>;
78+
expectType<true>(equalWrappedTupleIntersectionToBeNeverAndNever);
79+
80+
// `IsEqual` returns `false`, `SimpleIsEqual` returns `true`.
81+
declare const equalWrappedTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? SimpleIsEqual<(BranchOnWrappedTupleMatches<[Tpl]> & BranchOnWrappedTupleDoesNotMatch<[Tpl]>), never> : never;
82+
expectType<false>(equalWrappedTupleIntersectionToBeNeverAndNeverExpanded);
83+
84+
declare const equalTupleIntersectionToBeNeverAndNever: SimpleIsEqual<(BranchOnTupleMatches<[0, 2]> & BranchOnTupleDoesNotMatch<[0, 2]>), never>;
85+
expectType<true>(equalTupleIntersectionToBeNeverAndNever);
86+
87+
declare const equalTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? SimpleIsEqual<(BranchOnTupleMatches<Tpl> & BranchOnTupleDoesNotMatch<Tpl>), never> : never;
88+
expectType<true>(equalTupleIntersectionToBeNeverAndNeverExpanded);
89+
90+
declare const equalTupleIntersectionAndTuple: SimpleIsEqual<[{a: 1}] & [{a: 1}], [{a: 1}]>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
91+
expectType<true>(equalTupleIntersectionAndTuple);
92+
93+
// Test for Issue https://github.com/sindresorhus/type-fest/issues/1305
94+
type Assignability<T, U, _V extends SimpleIsEqual<T, U>> = any;
95+
type TestAssignability<T> = Assignability<T, T, true>;

0 commit comments

Comments
 (0)