Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 7 additions & 0 deletions source/is-equal.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type {IsNever} from './is-never.d.ts';
/**
Returns a boolean for whether the two given types are equal.

Expand Down Expand Up @@ -25,6 +26,12 @@ type Includes<Value extends readonly any[], Item> =
@category Utilities
*/
export type IsEqual<A, B> =
[A, B] extends [infer A, infer B]
? _IsEqual<A, B>
: false;

// This version fails the `equalWrappedTupleIntersectionToBeNeverAndNeverExpanded` test in `test-d/is-equal.ts`.
type _IsEqual<A, B> =
(<G>() => G extends A & G | G ? 1 : 2) extends
(<G>() => G extends B & G | G ? 1 : 2)
? true
Expand Down
77 changes: 63 additions & 14 deletions test-d/is-equal.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
import {expectType} from 'tsd';
import type {IsEqual} from '../index.d.ts';
import type {BuildTuple} from '../source/internal/index.d.ts';

const notEqualNumberAndString: IsEqual<number, string> = false;
expectType<false>(notEqualNumberAndString);
expectType<false>({} as IsEqual<number, string>);
expectType<true>({} as IsEqual<1, 1>);
expectType<false>({} as IsEqual<'A', 'B'>);
expectType<true>({} as IsEqual<'foo', 'foo'>);
expectType<false>({} as IsEqual<true, false>);
expectType<true>({} as IsEqual<false, false>);

const equalNumbers: IsEqual<1, 1> = true;
expectType<true>(equalNumbers);
expectType<false>({} as IsEqual<any, number>);
expectType<false>({} as IsEqual<'', never>);
expectType<true>({} as IsEqual<any, any>);
expectType<true>({} as IsEqual<never, never>);
expectType<false>({} as IsEqual<any, never>);
expectType<false>({} as IsEqual<never, any>);
expectType<false>({} as IsEqual<any, unknown>);
expectType<false>({} as IsEqual<[any], [never]>);
expectType<true>({} as IsEqual<[any], [any]>);
expectType<true>({} as IsEqual<[never], [never]>);

const notEqualAnyAndNumber: IsEqual<any, number> = false;
expectType<false>(notEqualAnyAndNumber);
expectType<false>({} as IsEqual<1 | 2, 1>);
expectType<false>({} as IsEqual<1 | 2, 2 | 3>);
expectType<true>({} as IsEqual<1 | 2, 2 | 1>);
expectType<false>({} as IsEqual<boolean, true>);

const notEqualUnionAndNumber: IsEqual<1 | 2, 1> = false;
expectType<false>(notEqualUnionAndNumber);
expectType<true>({} as IsEqual<{a: 1}, {a: 1}>);
expectType<false>({} as IsEqual<{a: 1}, {a?: 1}>);
expectType<false>({} as IsEqual<{a: 1}, {readonly a: 1}>);

const notEqualAnyAndNever: IsEqual<any, never> = false;
expectType<false>(notEqualAnyAndNever);
expectType<true>({} as IsEqual<[], []>);
expectType<true>({} as IsEqual<readonly [], readonly []>);
expectType<false>({} as IsEqual<readonly [], []>);
expectType<true>({} as IsEqual<number[], number[]>);
expectType<true>({} as IsEqual<readonly number[], readonly number[]>);
expectType<false>({} as IsEqual<readonly number[], number[]>);
expectType<true>({} as IsEqual<[string], [string]>);
expectType<false>({} as IsEqual<[string], [string, number]>);
expectType<false>({} as IsEqual<[0, 1] | [0, 2], [0, 2]>);

const notEqualArrayOfAnyAndArrayOfNever: IsEqual<[any], [never]> = false;
expectType<false>(notEqualArrayOfAnyAndArrayOfNever);
type LongTupleNumber = BuildTuple<50, 0>;
expectType<true>({} as IsEqual<LongTupleNumber, LongTupleNumber>);

type ReadonlyLongTupleNumber = Readonly<BuildTuple<50, 0>>;
expectType<true>({} as IsEqual<ReadonlyLongTupleNumber, ReadonlyLongTupleNumber>);

expectType<false>({} as IsEqual<ReadonlyLongTupleNumber, LongTupleNumber>);

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

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

type IntersectionType = IsEqual<{a: 1} | {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
type IntersectionType = IsEqual<{a: 1} & {a: 1}, {a: 1}>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
expectType<IntersectionType>(true);

// Test for PR https://github.com/sindresorhus/type-fest/pull/1231
type BranchOnWrappedTupleMatches<Tpl> = (Tpl extends [[0, 2]] ? 'Foo' : 'Bar');
type BranchOnWrappedTupleDoesNotMatch<Tpl> = (Tpl extends [[0, 1]] ? 'Foo' : 'Bar');
type BranchOnTupleMatches<Tpl> = (Tpl extends [0, 2] ? 'Foo' : 'Bar');
type BranchOnTupleDoesNotMatch<Tpl> = (Tpl extends [0, 1] ? 'Foo' : 'Bar');

declare const equalWrappedTupleIntersectionToBeNeverAndNever: IsEqual<(BranchOnWrappedTupleMatches<[[0, 2]]> & BranchOnWrappedTupleDoesNotMatch<[[0, 2]]>), never>;
expectType<true>(equalWrappedTupleIntersectionToBeNeverAndNever);

declare const equalWrappedTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? IsEqual<(BranchOnWrappedTupleMatches<[Tpl]> & BranchOnWrappedTupleDoesNotMatch<[Tpl]>), never> : never;
expectType<true>(equalWrappedTupleIntersectionToBeNeverAndNeverExpanded);

declare const equalTupleIntersectionToBeNeverAndNever: IsEqual<(BranchOnTupleMatches<[0, 2]> & BranchOnTupleDoesNotMatch<[0, 2]>), never>;
expectType<true>(equalTupleIntersectionToBeNeverAndNever);

declare const equalTupleIntersectionToBeNeverAndNeverExpanded: [0, 2] extends infer Tpl ? IsEqual<(BranchOnTupleMatches<Tpl> & BranchOnTupleDoesNotMatch<Tpl>), never> : never;
expectType<true>(equalTupleIntersectionToBeNeverAndNeverExpanded);

declare const equalTupleIntersectionAndTuple: IsEqual<[{a: 1}] & [{a: 1}], [{a: 1}]>; // eslint-disable-line @typescript-eslint/no-duplicate-type-constituents
expectType<true>(equalTupleIntersectionAndTuple);