UNPKG

rivo

Version:

🤖 The ultimate library you need for composable type-level programming in TypeScript, powered by HKT.

377 lines (357 loc) • 10.4 kB
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, U> = 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<T = unknown> { __lazy: T; } /** * Get the wrapped type of lazied type `L`. * * @see {@link Lazied} */ export type Lazied$Get<L extends Lazied<unknown>> = L["__lazy"]; /********************** * Boolean operations * **********************/ /** * Reverse a boolean value. */ export type Not<B extends boolean> = 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 boolean, B extends boolean> = 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 boolean, B extends boolean> = 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<readonly string[], ...>` below checks that * // the return type should extend `readonly string[]`. * type ToChars<S extends string> = Assert< * readonly string[], * S extends `${infer C}${infer R}` * ? readonly [C, ...ToChars<R>] * : readonly [] * >; * * // If you write something like this: * type ToChars<S extends string> = 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<Type, Expect extends Type> = Expect; /** * @see {@link Assert} */ export type AssertUnknown<Expect> = Expect; /** * @see {@link Assert} */ export type AssertBool<Expect extends boolean> = Expect; /** * @see {@link Assert} */ export type AssertNum<Expect extends number> = Expect; /** * @see {@link Assert} */ export type AssertInt<Expect extends Int> = Expect; /** * @see {@link Assert} */ export type AssertNat<Expect extends Nat> = Expect; /** * @see {@link Assert} */ export type AssertOrdering<Expect extends Ordering> = Expect; /** * @see {@link Assert} */ export type AssertStr<Expect extends string> = Expect; /** * @see {@link Assert} */ export type AssertObj<Expect extends object> = Expect; /**************** * Control flow * ****************/ /** * If `Cond` is `true`, return `Then`, otherwise return `Else`. */ export type If<Cond extends boolean, Then, Else> = Cond extends true ? Then : Else; /************* * Predicate * *************/ /** * Check if `T` extends `U`. */ export type Extends<T, U> = T extends U ? true : false; /** * Check if `T` is `any`. */ export type IsAny<T> = 0 extends 1 & T ? true : false; /** * Check if `T` is `never`. */ export type IsNever<T> = [T] extends [never] ? true : false; /** * Check if `T` is `unknown`. */ export type IsUnknown<T> = 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 List<boolean>> = BS extends BS ? IsTuple<BS> 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>] ? boolean extends Head ? boolean : Head extends false ? false : All<Tail> : 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 List<boolean>> = BS extends BS ? IsTuple<BS> 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>] ? boolean extends Head ? boolean : Head extends true ? true : Any<Tail> : 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<T, U> = (<G>() => G extends T ? 1 : 2) extends <G>() => G extends U ? 1 : 2 ? true : false; type IsLikelyClassType<T> = T extends { prototype: unknown } ? IsAny<T["prototype"]> 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 [42, "foo"]>; * // ^?: readonly (number | string)[] * type R5 = Broaden<{ foo: 42; bar: "foo" }>; * // ^?: { foo: number; bar: string; } * type R6 = Broaden<Date>; // <- Internal types are not broadened * // ^?: Date * class Foo { * constructor(public foo: 42, public bar: "foo") {} * } * type R7 = Broaden<typeof Foo>; // <- Class types are not broadened * // ^?: typeof Foo * ``` */ export type Broaden<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T extends bigint ? bigint : T extends Array<infer U> ? Array<Broaden<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<Broaden<U>> : T extends object ? // prettier-ignore T extends ((...args: any) => any) | typeof globalThis | Date | RegExp | Error | Promise<any> | Map<any, any> | Set<any> | WeakMap<any, any> | WeakSet<any> | ArrayBuffer | DataView | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array | BigInt64Array | BigUint64Array | ArrayBufferView | ReadonlyMap<any, any> | ReadonlySet<any> ? T : IsLikelyClassType<T> extends true ? T : { [K in keyof T]: T[K] extends ( string | number | boolean | bigint | ReadonlyArray<string | number | boolean | bigint> ) ? Broaden<T[K]> : T[K]; } extends infer R ? Eq<R, T> 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<number, number>; * // ^?: false * type R3 = LiteralCheck<1, string>; * // ^?: false * ``` */ type LiteralCheck<T, LiteralType extends Primitive> = IsNever<T> 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<bigint, number | bigint>; * // ^?: false * ``` */ type LiteralChecks<T, LiteralUnionType> = // Conditional type to force union distribution. // If `T` is none of the literal types in the union `LiteralUnionType`, then `LiteralCheck<T, LiteralType>` 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<T, LiteralUnionType> : never) extends false ? false : true; export type IsStringLiteral<T> = LiteralCheck<T, string>; export type IsNumberLiteral<T> = LiteralCheck<T, number>; export type IsBigIntLiteral<T> = LiteralCheck<T, bigint>; export type IsNumericLiteral<T> = LiteralChecks<T, number | bigint>; export type IsBooleanLiteral<T> = LiteralCheck<T, boolean>; export type IsSymbolLiteral<T> = LiteralCheck<T, symbol>; export type IsPropertyKeyLiteral<T> = LiteralChecks<T, PropertyKey>; /** * Check if the given type `T` is a literal type. */ export type IsLiteral<T> = [T] extends [Primitive] ? IsLiteralUnion<T> extends false ? false : true : false; type IsLiteralUnion<T> = | IsStringLiteral<T> | IsNumericLiteral<T> | IsBooleanLiteral<T> | IsSymbolLiteral<T>;