Skip to content

Commit

Permalink
Add IsEqual type (#522)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
doz13189 and sindresorhus committed Dec 25, 2022
1 parent 6950398 commit e5a3a57
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 17 deletions.
1 change: 1 addition & 0 deletions index.d.ts
Expand Up @@ -74,6 +74,7 @@ export type {RequiredKeysOf} from './source/required-keys-of';
export type {HasRequiredKeys} from './source/has-required-keys';
export type {Spread} from './source/spread';
export type {TupleToUnion} from './source/tuple-to-union';
export type {IsEqual} from './source/is-equal';

// Template literal types
export type {CamelCase} from './source/camel-case';
Expand Down
1 change: 1 addition & 0 deletions readme.md
Expand Up @@ -169,6 +169,7 @@ Click the type names for complete docs.
- [`RequiredKeysOf`](source/required-keys-of.d.ts) - Extract all required keys from the given type.
- [`HasRequiredKeys`](source/has-required-keys.d.ts) - Create a `true`/`false` type depending on whether the given type has any required fields.
- [`Spread`](source/spread.d.ts) - Mimic the type inferred by TypeScript when merging two objects or two arrays/tuples using the spread syntax.
- [`IsEqual`](source/is-equal.d.ts) - Returns a boolean for whether the two given types are equal.

### JSON

Expand Down
2 changes: 1 addition & 1 deletion source/conditional-pick-deep.d.ts
@@ -1,5 +1,5 @@
import type {Opaque} from './opaque';
import type {IsEqual} from './internal';
import type {IsEqual} from './is-equal';
import type {ConditionalExcept} from './conditional-except';
import type {ConditionalSimplifyDeep} from './conditional-simplify';

Expand Down
2 changes: 1 addition & 1 deletion source/except.d.ts
@@ -1,4 +1,4 @@
import type {IsEqual} from './internal';
import type {IsEqual} from './is-equal';

/**
Filter out keys from an object.
Expand Down
2 changes: 1 addition & 1 deletion source/includes.d.ts
@@ -1,4 +1,4 @@
import type {IsEqual} from './internal';
import type {IsEqual} from './is-equal';

/**
Returns a boolean for whether the given array includes the given item.
Expand Down
12 changes: 0 additions & 12 deletions source/internal.d.ts
@@ -1,18 +1,6 @@
import type {Primitive} from './primitive';
import type {Simplify} from './simplify';

/**
Returns a boolean for whether the two given types are equal.
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
*/
export type IsEqual<T, U> =
(<G>() => G extends T ? 1 : 2) extends
(<G>() => G extends U ? 1 : 2)
? true
: false;

/**
Infer the length of the given array `<T>`.
Expand Down
30 changes: 30 additions & 0 deletions source/is-equal.d.ts
@@ -0,0 +1,30 @@
/**
Returns a boolean for whether the two given types are equal.
@link https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
@link https://stackoverflow.com/questions/68961864/how-does-the-equals-work-in-typescript/68963796#68963796
Use-cases:
- If you want to make a conditional branch based on the result of a comparison of two types.
@example
```
import type {IsEqual} from 'type-fest';
// This type returns a boolean for whether the given array includes the given item.
// `IsEqual` is used to compare the given array at position 0 and the given item and then return true if they are equal.
type Includes<Value extends readonly any[], Item> =
Value extends readonly [Value[0], ...infer rest]
? IsEqual<Value[0], Item> extends true
? true
: Includes<rest, Item>
: false;
```
@category Utilities
*/
export type IsEqual<A, B> =
(<G>() => G extends A ? 1 : 2) extends
(<G>() => G extends B ? 1 : 2)
? true
: false;
3 changes: 2 additions & 1 deletion source/multidimensional-array.d.ts
@@ -1,4 +1,5 @@
import type {IsEqual, Subtract} from './internal';
import type {Subtract} from './internal';
import type {IsEqual} from './is-equal';

type Recursive<T> = Array<Recursive<T>>;

Expand Down
3 changes: 2 additions & 1 deletion source/multidimensional-readonly-array.d.ts
@@ -1,4 +1,5 @@
import type {IsEqual, Subtract} from './internal';
import type {Subtract} from './internal';
import type {IsEqual} from './is-equal';

type Recursive<T> = ReadonlyArray<Recursive<T>>;

Expand Down
28 changes: 28 additions & 0 deletions test-d/is-equal.ts
@@ -0,0 +1,28 @@
import {expectError, expectType} from 'tsd';
import type {IsEqual} from '../index';

const notEqualNumberAndString: IsEqual<number, string> = false;
expectType<false>(notEqualNumberAndString);

const equalNumbers: IsEqual<1, 1> = true;
expectType<true>(equalNumbers);

const notEqualAnyAndNumber: IsEqual<any, number> = false;
expectType<false>(notEqualAnyAndNumber);

const notEqualUnionAndNumber: IsEqual<1 | 2, 1> = false;
expectType<false>(notEqualUnionAndNumber);

const notEqualAnyAndNever: IsEqual<any, never> = false;
expectType<false>(notEqualAnyAndNever);

const notEqualArrayOfAnyAndArrayOfNever: IsEqual<[any], [never]> = false;
expectType<false>(notEqualArrayOfAnyAndArrayOfNever);

declare const anything: any;

// Missing all generic parameters.
expectError<IsEqual>(anything);

// Missing `Y` generic parameter.
expectError<IsEqual<number>>(anything);

0 comments on commit e5a3a57

Please sign in to comment.