Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IsEqual type #522

Merged
merged 11 commits into from Dec 25, 2022
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.

doz13189 marked this conversation as resolved.
Show resolved Hide resolved
@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> =
doz13189 marked this conversation as resolved.
Show resolved Hide resolved
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);