From e5a3a57ea0764f01dade561243927af000291842 Mon Sep 17 00:00:00 2001 From: Takahiro Kato Date: Mon, 26 Dec 2022 07:56:16 +0900 Subject: [PATCH] Add `IsEqual` type (#522) Co-authored-by: Sindre Sorhus --- index.d.ts | 1 + readme.md | 1 + source/conditional-pick-deep.d.ts | 2 +- source/except.d.ts | 2 +- source/includes.d.ts | 2 +- source/internal.d.ts | 12 --------- source/is-equal.d.ts | 30 +++++++++++++++++++++ source/multidimensional-array.d.ts | 3 ++- source/multidimensional-readonly-array.d.ts | 3 ++- test-d/is-equal.ts | 28 +++++++++++++++++++ 10 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 source/is-equal.d.ts create mode 100644 test-d/is-equal.ts diff --git a/index.d.ts b/index.d.ts index 68338a783..a48013f5f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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'; diff --git a/readme.md b/readme.md index 433391fa4..6a5d0b32c 100644 --- a/readme.md +++ b/readme.md @@ -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 diff --git a/source/conditional-pick-deep.d.ts b/source/conditional-pick-deep.d.ts index cb6ccf90f..30d09e738 100644 --- a/source/conditional-pick-deep.d.ts +++ b/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'; diff --git a/source/except.d.ts b/source/except.d.ts index dab21d54c..bf1dd4154 100644 --- a/source/except.d.ts +++ b/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. diff --git a/source/includes.d.ts b/source/includes.d.ts index 610b850c0..b269dd48e 100644 --- a/source/includes.d.ts +++ b/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. diff --git a/source/internal.d.ts b/source/internal.d.ts index d92b7ca81..329cc9fdb 100644 --- a/source/internal.d.ts +++ b/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 = - (() => G extends T ? 1 : 2) extends - (() => G extends U ? 1 : 2) - ? true - : false; - /** Infer the length of the given array ``. diff --git a/source/is-equal.d.ts b/source/is-equal.d.ts new file mode 100644 index 000000000..4a64dda57 --- /dev/null +++ b/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 [Value[0], ...infer rest] + ? IsEqual extends true + ? true + : Includes + : false; +``` + +@category Utilities +*/ +export type IsEqual = + (() => G extends A ? 1 : 2) extends + (() => G extends B ? 1 : 2) + ? true + : false; diff --git a/source/multidimensional-array.d.ts b/source/multidimensional-array.d.ts index f100bd1b9..f2f553968 100644 --- a/source/multidimensional-array.d.ts +++ b/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 = Array>; diff --git a/source/multidimensional-readonly-array.d.ts b/source/multidimensional-readonly-array.d.ts index 92c4e2f05..5896cd2e2 100644 --- a/source/multidimensional-readonly-array.d.ts +++ b/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 = ReadonlyArray>; diff --git a/test-d/is-equal.ts b/test-d/is-equal.ts new file mode 100644 index 000000000..242798d01 --- /dev/null +++ b/test-d/is-equal.ts @@ -0,0 +1,28 @@ +import {expectError, expectType} from 'tsd'; +import type {IsEqual} from '../index'; + +const notEqualNumberAndString: IsEqual = false; +expectType(notEqualNumberAndString); + +const equalNumbers: IsEqual<1, 1> = true; +expectType(equalNumbers); + +const notEqualAnyAndNumber: IsEqual = false; +expectType(notEqualAnyAndNumber); + +const notEqualUnionAndNumber: IsEqual<1 | 2, 1> = false; +expectType(notEqualUnionAndNumber); + +const notEqualAnyAndNever: IsEqual = false; +expectType(notEqualAnyAndNever); + +const notEqualArrayOfAnyAndArrayOfNever: IsEqual<[any], [never]> = false; +expectType(notEqualArrayOfAnyAndArrayOfNever); + +declare const anything: any; + +// Missing all generic parameters. +expectError(anything); + +// Missing `Y` generic parameter. +expectError>(anything);