import type { IsTuple } from "./Ary/IsTuple"; import type { List } from "./List"; import type { Int, Nat } from "./Num"; import type { Ordering } from "./typeclass/Ord"; /** * A type that represents a primitive value. */ export type Primitive = null | undefined | string | number | boolean | symbol | bigint; /** * Force casting `T` to `U`. * * **It is not recommended to use simply for eliminating type errors, * as it reduced the ability of error recovery.** * * If you're sure an error won't happen but TS doesn't know, * use `@ts-expect-error` instead. */ export type Cast = T extends U ? T : U; /********************* * Lazied evaluation * *********************/ /** * Wrap `T` to make it lazily evaluated by TS. * * It is useful when creating self-referential union types, especially for implementing type * classes. * * Unwrap the type by {@link Lazied$Get}. */ export interface Lazied { __lazy: T; } /** * Get the wrapped type of lazied type `L`. * * @see {@link Lazied} */ export type Lazied$Get> = L["__lazy"]; /********************** * Boolean operations * **********************/ /** * Reverse a boolean value. */ export type Not = boolean extends B ? boolean : B extends true ? false : true; /** * Return `true` if both `A` and `B` are `true`, otherwise `false`. */ export type And = A extends true ? B extends true ? true : false : false; /** * Return `true` if either `A` or `B` is `true`, otherwise `false`. */ export type Or = A extends true ? true : B extends true ? true : false; /************* * Assertion * *************/ /** * Assert that `Expect` extends `Type`. * * It is used to explicitly specify the `ReturnType` of a generic. * * There're several predefined aliases for common types like `AssertBool`, `AssertNum`, etc. * * @example * ```typescript * // The `Assert` below checks that * // the return type should extend `readonly string[]`. * type ToChars = Assert< * readonly string[], * S extends `${infer C}${infer R}` * ? readonly [C, ...ToChars] * : readonly [] * >; * * // If you write something like this: * type ToChars = Assert< * readonly string[], * S extends `${infer C}${infer R}` ? C : never * //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * // Type 'S extends `${infer C}${infer R}` ? C : never' does not satisfy the constraint 'readonly string[]'. * >; * ``` */ export type Assert = Expect; /** * @see {@link Assert} */ export type AssertUnknown = Expect; /** * @see {@link Assert} */ export type AssertBool = Expect; /** * @see {@link Assert} */ export type AssertNum = Expect; /** * @see {@link Assert} */ export type AssertInt = Expect; /** * @see {@link Assert} */ export type AssertNat = Expect; /** * @see {@link Assert} */ export type AssertOrdering = Expect; /** * @see {@link Assert} */ export type AssertStr = Expect; /** * @see {@link Assert} */ export type AssertObj = Expect; /**************** * Control flow * ****************/ /** * If `Cond` is `true`, return `Then`, otherwise return `Else`. */ export type If = Cond extends true ? Then : Else; /************* * Predicate * *************/ /** * Check if `T` extends `U`. */ export type Extends = T extends U ? true : false; /** * Check if `T` is `any`. */ export type IsAny = 0 extends 1 & T ? true : false; /** * Check if `T` is `never`. */ export type IsNever = [T] extends [never] ? true : false; /** * Check if `T` is `unknown`. */ export type IsUnknown = unknown extends T ? true : false; /** * Check if all elements of a tuple of `boolean`s are `true`. * * @example * ```typescript * type R1 = All<[true, true, true]>; * // ^?: true * type R2 = All<[true, false, true]>; * // ^?: false * type R3 = All<[true, true, true, ...true[]]>; * // ^?: true * type R4 = All<[true, true, true, ...boolean[]]>; * // ^?: boolean * type R5 = All<[true, boolean, true]>; * // ^?: boolean * ``` */ export type All> = BS extends BS ? IsTuple extends false ? BS[number] extends true ? true : BS[number] extends false ? false : boolean : BS extends readonly [infer Head extends boolean, ...infer Tail extends List] ? boolean extends Head ? boolean : Head extends false ? false : All : true : never; /** * Check if any element of a tuple of `boolean`s is `true`. * * @example * ```typescript * type R1 = Any<[false, false, false]>; * // ^?: false * type R2 = Any<[true, false, true]>; * // ^?: false * type R3 = Any<[true, false, true, ...true[]]>; * // ^?: true * type R4 = Any<[false, false, false, ...false[]]>; * // ^?: false * type R5 = Any<[true, true, true, ...boolean[]]>; * // ^?: boolean * type R6 = Any<[true, boolean, true]>; * // ^?: true * ``` */ export type Any> = BS extends BS ? IsTuple extends false ? BS[number] extends false ? false : BS[number] extends true ? true : boolean : BS extends readonly [infer Head extends boolean, ...infer Tail extends List] ? boolean extends Head ? boolean : Head extends true ? true : Any : false : never; /** * Checks whether `T` exactly equals `U`. * * @example * ```typescript * type R1 = Eq<1, 1>; * // ^?: true * type R2 = Eq<1, number>; * // ^?: false * type R3 = Eq<1, 1 | 2>; * // ^?: false * ``` */ export type Eq = (() => G extends T ? 1 : 2) extends () => G extends U ? 1 : 2 ? true : false; type IsLikelyClassType = T extends { prototype: unknown } ? IsAny extends true ? false : true : false; /** * Broaden the type of `T` to a more general type. * * @example * ```typescript * type R1 = Broaden<42>; * // ^?: number * type R2 = Broaden<"foo" | false>; * // ^?: string | boolean * type R3 = Broaden<1336[]>; * // ^?: number[] * type R4 = Broaden; * // ^?: readonly (number | string)[] * type R5 = Broaden<{ foo: 42; bar: "foo" }>; * // ^?: { foo: number; bar: string; } * type R6 = Broaden; // <- Internal types are not broadened * // ^?: Date * class Foo { * constructor(public foo: 42, public bar: "foo") {} * } * type R7 = Broaden; // <- Class types are not broadened * // ^?: typeof Foo * ``` */ export type Broaden = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends bigint ? bigint : T extends Array ? Array> : T extends ReadonlyArray ? ReadonlyArray> : T extends object ? // prettier-ignore T extends ((...args: any) => any) | typeof globalThis | Date | RegExp | Error | Promise | Map | Set | WeakMap | WeakSet | ArrayBuffer | DataView | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array | ArrayBufferView | ReadonlyMap | ReadonlySet ? T : IsLikelyClassType extends true ? T : { [K in keyof T]: T[K] extends ( string | number | boolean | bigint | ReadonlyArray ) ? Broaden : T[K]; } extends infer R ? Eq extends true ? T : R : never : T; /** * Check if the given type `T` is the specified `LiteralType`. * * Modified from [type-fest](https://github.com/sindresorhus/type-fest/blob/main/source/is-literal.d.ts). * * @example * ```typescript * type R1 = LiteralCheck<1, number>; * // ^?: true * type R2 = LiteralCheck; * // ^?: false * type R3 = LiteralCheck<1, string>; * // ^?: false * ``` */ type LiteralCheck = IsNever extends ( false // Must be wider than `never` ) ? [T] extends ( [LiteralType & infer U] // Remove any branding ) ? [U] extends ( [LiteralType] // Must be narrower than `LiteralType` ) ? [LiteralType] extends ( [U] // Cannot be wider than `LiteralType` ) ? false : true : false : false : false; /** * Check if the given type `T` is one of the specified literal types in `LiteralUnionType`. * * Modified from [type-fest](https://github.com/sindresorhus/type-fest/blob/main/source/is-literal.d.ts). * * @example * ```typescript * type R1 = LiteralChecks<1, number | bigint>; * // ^?: true * type R2 = LiteralChecks<1n, number | bigint>; * // ^?: false * type R3 = LiteralChecks; * // ^?: false * ``` */ type LiteralChecks = // Conditional type to force union distribution. // If `T` is none of the literal types in the union `LiteralUnionType`, then `LiteralCheck` will evaluate to `false` for the whole union. // If `T` is one of the literal types in the union, it will evaluate to `boolean` (i.e. `true | false`) (LiteralUnionType extends Primitive ? LiteralCheck : never) extends false ? false : true; export type IsStringLiteral = LiteralCheck; export type IsNumberLiteral = LiteralCheck; export type IsBigIntLiteral = LiteralCheck; export type IsNumericLiteral = LiteralChecks; export type IsBooleanLiteral = LiteralCheck; export type IsSymbolLiteral = LiteralCheck; export type IsPropertyKeyLiteral = LiteralChecks; /** * Check if the given type `T` is a literal type. */ export type IsLiteral = [T] extends [Primitive] ? IsLiteralUnion extends false ? false : true : false; type IsLiteralUnion = | IsStringLiteral | IsNumericLiteral | IsBooleanLiteral | IsSymbolLiteral;