diff --git a/source/and.d.ts b/source/and.d.ts index b6d94124a..452442a2d 100644 --- a/source/and.d.ts +++ b/source/and.d.ts @@ -1,4 +1,6 @@ -import type {IsEqual} from './is-equal.d.ts'; +import type {IsTrue} from './internal/type.d.ts'; +import type {Includes} from './includes.d.ts'; +import type {IsAny} from './is-any.d.ts'; /** Returns a boolean for whether two given types are both true. @@ -14,12 +16,50 @@ And; And; //=> false + +And; +//=> never + +And; +//=> false + +``` + +@see Or, AndAll +*/ +export type And = AndAll<[A, B]>; + +/** +Returns a boolean for whether All given types are true. + +Use-case: Constructing complex conditional types where multiple conditions must be satisfied. + +@example +``` +import type {AndAll} from 'type-fest'; + +AndAll<[true, true, true]>; +//=> true + +AndAll<[true, false, true]>; +//=> false + +AndAll<[true, boolean, true]>; +//=> never + +AndAll<[false, boolean, true]>; +//=> false + ``` -@see {@link Or} +@see And, OrAll */ -export type And = [A, B][number] extends true - ? true - : true extends [IsEqual, IsEqual][number] - ? false - : never; +export type AndAll = ( + IsAny extends false + ? IsTrue extends true + ? true + : Includes extends true + ? false + : never + : never +); diff --git a/source/internal/type.d.ts b/source/internal/type.d.ts index 85dac83ff..f6b2c149c 100644 --- a/source/internal/type.d.ts +++ b/source/internal/type.d.ts @@ -1,7 +1,8 @@ -import type {If} from '../if.d.ts'; -import type {IsAny} from '../is-any.d.ts'; -import type {IsNever} from '../is-never.d.ts'; import type {Primitive} from '../primitive.d.ts'; +import type {IsNever} from '../is-never.d.ts'; +import type {IsAny} from '../is-any.d.ts'; +import type {Or} from '../or.d.ts'; +import type {If} from '../if.d.ts'; /** Matches any primitive, `void`, `Date`, or `RegExp` value. @@ -13,6 +14,13 @@ Matches non-recursive types. */ export type NonRecursiveType = BuiltIns | Function | (new (...arguments_: any[]) => unknown); +/** + * Checks if one type extends another. Note: this is not quite the same as `Left extends Right` because: + * 1. If either type is `never`, the result is `true` iff the other type is also `never`. + * 2. Types are wrapped in a 1-tuple so that union types are not distributed - instead we consider `string | number` to _not_ extend `number`. If we used `Left extends Right` directly you would get `Extends` => `false | true` => `boolean`. + */ +export type Extends = IsNever extends true ? IsNever : [Left] extends [Right] ? true : false; + /** Returns a boolean for whether the two given types extends the base type. */ @@ -40,9 +48,19 @@ export type HasMultipleCallSignatures unknow : false; /** -Returns a boolean for whether the given `boolean` is not `false`. +Returns a boolean for whether the given `boolean` Union containe's `false`. */ -export type IsNotFalse = [T] extends [false] ? false : true; +export type IsNotFalse = Not>; + +/** +Returns a boolean for whether the given `boolean` Union members are all `true`. +*/ +export type IsTrue = Extends; + +/** +Returns a boolean for whether the given `boolean` Union members are all `false`. +*/ +export type IsFalse = Extends; /** Returns a boolean for whether the given type is primitive value or primitive type. @@ -59,7 +77,7 @@ IsPrimitive //=> false ``` */ -export type IsPrimitive = [T] extends [Primitive] ? true : false; +export type IsPrimitive = Extends; /** Returns a boolean for whether A is false. @@ -99,3 +117,8 @@ type C = IfNotAnyOrNever; */ export type IfNotAnyOrNever = If, IfAny, If, IfNever, IfNotAnyOrNever>>; + +/** +Determines if a type is either `never` or `any`. +*/ +export type IsAnyOrNever = Or, IsNever>; diff --git a/source/or.d.ts b/source/or.d.ts index 3e393e197..e909673a9 100644 --- a/source/or.d.ts +++ b/source/or.d.ts @@ -1,4 +1,6 @@ -import type {IsEqual} from './is-equal.d.ts'; +import type {IsFalse} from './internal/type.d.ts'; +import type {Includes} from './includes.d.ts'; +import type {IsAny} from './is-any.d.ts'; /** Returns a boolean for whether either of two given types are true. @@ -14,12 +16,50 @@ Or; Or; //=> false + +Or; +//=> true + +Or; +//=> never + +``` + +@see And, OrAll +*/ +export type Or = OrAll<[A, B]>; + +/** +Returns a boolean for whether either of All given types are true. + +Use-case: Constructing complex conditional types where multiple conditions must be satisfied. + +@example +``` +import type {OrAll} from 'type-fest'; + +OrAll<[true, false, true]>; +//=> true + +OrAll<[false, false, false]>; +//=> false + +OrAll<[true, boolean, true]>; +//=> true + +OrAll<[false, boolean, true]>; +//=> never + ``` -@see {@link And} +@see Or, AndAll */ -export type Or = [A, B][number] extends false - ? false - : true extends [IsEqual, IsEqual][number] - ? true - : never; +export type OrAll = ( + IsAny extends false + ? IsFalse extends true + ? false + : Includes extends true + ? true + : never + : never +); diff --git a/test-d/and.ts b/test-d/and.ts index ea30eb183..6bc67a4aa 100644 --- a/test-d/and.ts +++ b/test-d/and.ts @@ -1,8 +1,9 @@ import {expectType} from 'tsd'; -import type {And} from '../source/and.d.ts'; +import type {And, AndAll} from '../source/and.d.ts'; declare const never: never; +// And expectType>(true); expectType>(false); expectType>(false); @@ -10,3 +11,43 @@ expectType>(false); expectType>(never); expectType>(false); expectType>(never); + +expectType>(never); +expectType>(never); +expectType>(false); +expectType>(false); +expectType>(true); +expectType>(true); +expectType>(never); + +// AndAll +expectType>(true); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); + +// @ts-expect-error +expectType({} as any); +expectType>(never); +expectType>(never); +expectType>(never); + +// Single value +expectType>(true); +expectType>(false); +expectType>(never); +expectType>(never); +expectType>(never); + +// Test if boolean is position dependent +expectType>(never); +expectType>(never); +expectType>(never); +expectType>(never); + +expectType>(false); +expectType>(false); +expectType>(false); +expectType>(false); diff --git a/test-d/or.ts b/test-d/or.ts index a7377a592..0bcd335e5 100644 --- a/test-d/or.ts +++ b/test-d/or.ts @@ -1,8 +1,9 @@ import {expectType} from 'tsd'; -import type {Or} from '../source/or.d.ts'; +import type {Or, OrAll} from '../source/or.d.ts'; declare const never: never; +// Or expectType>(true); expectType>(true); expectType>(true); @@ -10,3 +11,43 @@ expectType>(false); expectType>(true); expectType>(never); expectType>(never); + +expectType>(never); +expectType>(never); +expectType>(false); +expectType>(false); +expectType>(true); +expectType>(true); +expectType>(never); + +// OrAll +expectType>(true); +expectType>(true); +expectType>(true); +expectType>(false); +expectType>(true); +expectType>(true); + +// @ts-expect-error +expectType({} as any); +expectType>(never); +expectType>(never); +expectType>(never); + +// Single value +expectType>(true); +expectType>(false); +expectType>(never); +expectType>(never); +expectType>(never); + +// Test if boolean is position dependent +expectType>(true); +expectType>(true); +expectType>(true); +expectType>(true); + +expectType>(never); +expectType>(never); +expectType>(never); +expectType>(never);