UNPKG

ts-roids

Version:
2,005 lines (1,985 loc) 82.4 kB
/** * Used to display an error message instead of never, for better readability * @export type {Message} */ type Message<T extends string> = T; /** * * Represents a type that can either be ``null`` or ``undefined``. * @export type {Nullable} */ type Nullable = null | undefined; /** * Represents a type that can hold any numeric value: number or a bigint. * @export type {Numeric} */ type Numeric = number | bigint; /** * Represents all the primitive types in JavaScript. * - `Nullable`: A value that can be either null or undefined. * - `Numeric`: A value that can be either a number or a bigint. * - `string`: Represents textual data. * - `boolean`: Represents a logical value (true or false). * - `symbol`: Represents a unique and immutable value. */ type Primitive = string | boolean | symbol | Nullable | Numeric; /** * Represents a type that includes falsy values in JavaScript. * Falsy values are those that coerce to false when used in a boolean context. * This includes `false`, an empty string (`''`), numeric zero (`0`), `null`, * and `undefined`. */ type Falsy = false | '' | 0 | Nullable; /** * This type is used to describe constructor functions or classes * that can be invoked using the `new` keyword. */ type Newable = { new (...args: any[]): any; }; /** * Describes any function accepting any arguments * and returning any value. */ type AnyFunction = (...args: any[]) => any; /** * Describes any function accepting and retruning `unknown`s */ type UnknownFunction = (...args: unknown[]) => unknown; /** * `Optional<T>` is similar to Python's `Optional` and Rust's `Option` types. * It promotes more predictable code, * by enforcing explicit handling of optional scenarios, e.g: requiring functions * to return `null` specifically when a value is absent. */ type Optional<T> = T | null; /** Represnets a type that might be nullable, as in it might be `null` or `undefined`. */ type Maybe<T> = T | Nullable; type MaybeUnknown<T> = T | unknown; type MaybeUndefined<T> = T | undefined; /** * Presents any non-nullish value */ type EmptyObject = NonNullable<unknown>; /** * Represents a type that is not assignable to `V`. * @example */ type NotAssignableTo<U, V> = U extends V ? never : U; /** * Conditional type: if the condition `C` is `true`, return `Do`, otherwise return `Else`. * @example * ````ts If<IsNever<never>, true, false>; // true If<Not<IsNever<never>>, true, false>; // false * ```` */ type If<C extends boolean, Do, Else> = C extends true ? Do : Else; /** * Negates a boolean type `B`. * @example Not<true>; // false Not<false>; // true */ type Not<B extends boolean> = B extends true ? false : true; /** * Logical AND between two boolean types `B1` and `B2`. * @example And<true, false>; // false And<true, true>; // true */ type And<B1 extends boolean, B2 extends boolean> = If<B1, If<B2, true, false>, false>; /** * Logical OR between two boolean types `B1` and `B2`. * @example Or<true, false>; // true Or<false, false>; // false */ type Or<B1 extends boolean, B2 extends boolean> = If<B1, true, If<B2, true, false>>; /** * Logical NOR between two boolean types `B1` and `B2`. * Evaluates to `true` only if both `B1` and `B2` are `false`. * @example Nor<false, false>; // true Nor<true, false>; // false Nor<false, true>; // false Nor<true, true>; // false */ type Nor<A extends boolean, B extends boolean> = Not<Or<A, B>>; /** * Exclusive OR (XOR) between two boolean types `B1` and `B2`. * @example Xor<true, false>; // true Xor<true, true>; // false */ type Xor<B1 extends boolean, B2 extends boolean> = Or<And<B1, Not<B2>>, And<Not<B1>, B2>>; /** * Logical NAND between two boolean types `B1` and `B2`. * @example Nand<true, false>; // true Nand<true, true>; // false */ type Nand<B1 extends boolean, B2 extends boolean> = Not<And<B1, B2>>; /** * Logical XNOR between two boolean types `A` and `B`. * @example Xnor<true, false>; // false Xnor<true, true>; // true */ type Xnor<A extends boolean, B extends boolean> = Not<Xor<A, B>>; /** * Logical XAND between two boolean types `A` and `B`. (basically NOR) * @example Xand<false, false>; // true Xand<true, false>; // false Xand<false, true>; // false Xand<true, true>; // false */ type Xand<A extends boolean, B extends boolean> = Not<Xor<A, B>>; /** * Conditional type that checks if type `T` extends type `P`. * If `T` extends `P`, the type resolves to `Do`; otherwise `Else`. * @example * type Result1 = IfExtends<string, string | number, true, false>; // is true * type Result2 = IfExtends<number, string | number, true, false>; // is true * type Result3 = IfExtends<boolean, string | number, true, false>; // is false * * type IsString<T> = IfExtends<T, string, true, false>; * type IsNumber<T> = IfExtends<T, number, true, false>; * * type TestString = IsString<string>; // is true * type TestNumber = IsNumber<number>; // is true * type TestBoolean = IsNumber<boolean>; // is false */ type IfExtends<T, P, Do, Else> = T extends P ? Do : Else; /** * Conditional type that checks if type `T` is equal to type `P`. * If `T` is equal to `P`, the type resolves to `Do`, otherwise `Else`. * @example * type Result1 = IfEquals<string, string, true, false>; // is true * type Result2 = IfEquals<number, string, true, false>; // is false * type Result3 = IfEquals<boolean, boolean, true, false>; // is true * * type IsExactlyString<T> = IfEquals<T, string, true, false>; * type IsExactlyNumber<T> = IfEquals<T, number, true, false>; * * type TestString = IsExactlyString<string>; // is true * type TestNumber = IsExactlyNumber<number>; // is false * type TestBoolean = IsExactlyString<boolean>; // is false */ type IfEquals<T, P, Do, Else> = Equals<T, P> extends true ? Do : Else; /** * Conditional type that checks if two types `X` and `Y` are exactly equal. * If `X` is equal to `Y`, the type resolves to `true`; otherwise `false`. * @example * type Result1 = Equals<string, string>; // is true * type Result2 = Equals<number, string>; // is false * type Result3 = Equals<boolean | string, string | boolean>; // is true */ type Equals<X, Y> = (<T>() => T extends X ? true : false) extends <T>() => T extends Y ? true : false ? true : false; /** * As the name implies, it turns a union into an intersection * @example type T = NewType<'T', string>; type Result = UnionToIntersection< (() => 'foo') | ((baz: 88) => Optional<NewType<'T', string>>) > // Result: (() => 'foo') & ((baz: 88) => Optional<T>) type Result2 = UnionToIntersection< IsFalsy<0> | IsDeepImmutable<{ a: string; readonly b: string }> > // Result 2: IsFalsy<0> & IsDeepImmutable<{ a: string; readonly b: string }> => true & true => evaluates to true * */ type UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends (arg: infer I) => void ? I : never; type LastOf<T> = UnionToIntersection<T extends any ? () => T : never> extends () => infer R ? R : never; /** * `UnionToTuple<T>` converts a union type `T` into a tuple type while preserving the order. * This type is useful for scenarios where you need to work with the individual members of a union as an ordered list. * * @template T - The union type to convert into a tuple. * @template L - The last member of the union, used for recursive extraction. * @template N - A boolean that checks if the union is empty. * * @example * type TestUnion = 'a' | 'b' | 'c'; * type ResultTuple = UnionToTuple<TestUnion>; // Result: ['a', 'b', 'c'] */ type UnionToTuple<T, L = LastOf<T>, N = [T] extends [never] ? true : false> = N extends true ? [] : [...UnionToTuple<Exclude<T, L>>, L]; /** * Represents the keys of a given type `T`. * This type alias `Keys<T>` is equivalent to `keyof T`, * which retrieves the union type of keys (property names) of type `T`. * @returns Union type of keys (property names) of type `T`. * @example * type Person = { * name: string; * age: number; * email: string; * }; * * type PersonKeys = Keys<Person>; => "name" | "age" | "email" */ type Keys<T> = keyof T; /** * Represents the union type of values of properties in a given type `T`. * This type alias `Vals<T>` retrieves the union type of values corresponding * to the keys (property names) of type `T`. * @example * type Person = { * name: string; * age: number; * email: string; * }; * * type PersonValues = Vals<Person>; => string | number */ type Vals<T> = T[Keys<T>]; /** * Extracts keys from a type `T` that represent required properties. * Properties that are not marked with `?`. * @example * ```ts * type T = RequiredKeys<{ a: number; b?: string }> // Result: 'a' * ``` */ type RequiredKeys<T> = { [K in Keys<T>]-?: IfExtends<EmptyObject, Pick<T, K>, never, K>; }[Keys<T>]; /** * Why not call it ``OptionalKeys``? * ``Optional<T>`` in this library represents a type ``T`` that can be either ``T`` or ``null``. So creating * ``OptionalKeys`` type would entail removing any type that can be null, which is not the intention here. * * ``NonRequiredKeys<T>`` simply removes non required keys, as in any property of an object that is * marked with `?` operator * @example * ```ts * type T = NonRequiredKeys<{ a: number; b?: string }> // Result: 'b' * ``` */ type NonRequiredKeys<T> = Exclude<Keys<T>, RequiredKeys<T>>; /** * Represents a type that can either be a single value of type `T` or an array of values of type `T`. * @example * type T1 = EitherOneOrMany<number>; 10; // Valid * type T2 = EitherOneOrMany<number>; [20, 30]; // Also valid */ type EitherOneOrMany<T> = T | T[]; /** * Checks if a given type `T` is `Falsy`. * @returns `true` if `T` is a subtype of `Falsy`, otherwise `false`. * @example * type Falsy = IsFalsy<''>; // TestFalsy is tru` * type Truthy = IsFalsy<10>; // TestTruthy is false */ type IsFalsy<T> = IfExtends<T, Falsy, true, false>; /** * Checks if a given type `T` is a truthy value. * A truthy value is any value that is not a falsy value. * @returns `true` if `T` is not a subtype of `Falsy`, otherwise `false`. * @example * type TruthyString = IsTruthy<string>; // => true * type TruthyNumber = IsTruthy<10>; // => true * type FalsyNull = IsTruthy<null>; // => false * type FalsyEmptyString = IsTruthy<''>; => false */ type IsTruthy<T> = IfExtends<T, Exclude<T, Falsy>, true, false>; /** * Checks if a given type `T` is `never`. * The `never` type represents a value that never occurs, * for example a function that always errors out. * @returns `true` if `T` is `never`, otherwise `false`. * @example * type Never = IsNever<never>; => true * type NotNever = IsNever<string>; => false */ type IsNever<T> = Equals<T, never>; /** * A nullable type is a type that might be null, undefined or both * @returns * `true` if it is, else `false` */ type IsNullable<T> = IfExtends<T, Nullable, true, false>; /** * Checks if a given type `T` is `unknown`. * * Values of type `unknown` can hold any value, similar to `any`, but with stricter type safety. * Unlike `any`, you cannot perform operations directly on values of type `unknown` * without type assertion or type narrowing. * @returns `true` if `T` is `unknown`, otherwise `false`. * @example * type IsUnknownValue = IsUnknown<unknown>; // true * type IsNotUnknownValue = IsUnknown<string>; // also true * @remarks * > If you want `unknown` to be exact, use `IsExactlyUnknown` */ type IsUnknown<T> = IfExtends<T, unknown, true, false>; /** * Checks if a given type `T` is exactly `unknown`. * @example * ````ts IsExactlyUnknown<any | unknown>; // false, since the union evaluates to any IsExactlyAny<any | unknown>; // true IsExactlyUnknown<unknown | string> // true IsExactlyUnknown<string>; // flase * ```` */ type IsExactlyUnknown<T> = Equals<T, unknown>; /** * @returns `true` if `T` is `string`, otherwise `false`. */ type IsString<T> = IfExtends<T, string, true, false>; /** * @returns `true` if `T` is excatly `string`, otherwise `false`. * @example * ````ts IsExactlyString<number>; // false; IsExactlyString<any | string>; // false IsExactlyString<unknown | string>; // false * ```` */ type IsExactlyString<T> = Equals<T, string>; /** * @returns `true` if `T` is `boolean`, otherwise `false`. */ type IsBoolean<T> = IfExtends<T, boolean, true, false>; /** * A numeric type iincludes `number` and `bigint`. * @returns `true` if `T` is a numeric type, otherwise `false`. */ type IsNumeric<T> = IfExtends<T, Numeric, true, false>; /** * Is a given type `T` an array? * @returns `true` if `T` it is, otherwise `false`. * @example * ``` * IsArray<number[]>; // true * IsArray<string>; // false * ``` */ type IsArray<T> = IfExtends<T, unknown[], true, false>; /** * Type utility that checks if a given type `T` is an `AnyFunction` (any function type). * @template T The type to check. * @returns `true` if `T` is an `AnyFunction`, otherwise `false`. * An `AnyFunction` is defined as a function type that accepts any arguments and returns any value. * @example * ``` * IsAnyFunction<() => void>; // true (matches AnyFunction) * IsAnyFunction<(x: number) => string>; // true (matches AnyFunction) * IsAnyFunction<string>; // false (string is not a function type) * ``` */ type IsAnyFunction<T> = IfExtends<T, AnyFunction, true, false>; /** * Type utility that checks if a given type `T` is a `Function` (function type accepting `unknown` arguments and returning `unknown`). * @template T The type to check. * @returns `true` if `T` is a `Function`, otherwise `false`. * A `Function` is defined as a function type that accepts arguments of type `unknown` and returns a value of type `unknown`. * @example * ``` * IsFunction<() => void>; // true (matches Function) * IsFunction<(x: number) => string>; // true (matches Function) * IsFunction<string>; // false (string is not a function type) * ``` */ type IsFunction<T> = IfExtends<T, UnknownFunction, true, false>; /** * Checks if a given type `T` qualifies as an object. * @returns `true` if it is, otherwise `false`. * An object in this context is defined as a non-null object (excluding functions and arrays). * @example * ``` IsObject<object>; // true IsObject<{ name: string }>; // true IsObject<string>; // false IsObject<Function>; // true, yes, the built-in Function type is an interface with a bunch of methods, so yes it's an object. // if you want to use the function type use this: IsObject<UnknownFunction>; // false // or this IsObject<AnyFunction>; // false IsObject<any[]>; // false IsObject<null>; // false * ``` */ type IsObject<T> = And<IfExtends<T, object, true, false>, And<Not<IsFunction<T>>, Not<IsArray<T>>>>; /** * @returns `true` if `T` is `number`, otherwise `false`. */ type IsNumber<T> = IfExtends<T, number, true, false>; /** * @returns `true` if `T` is exactly of type `number`, otherwise `false`. * @example * ````ts IsExactlyNumber<any | number>; // false IsExactlyNumber<unknown | number> // false IsExactlyNumber<number> // true IsExactlyNumber<87> // false * ```` */ type IsExactlyNumber<T> = Equals<T, number>; /** * @returns `true` if `T` is `bigint`, otherwise `false`. */ type IsBigInt<T> = IfExtends<T, bigint, true, false>; /** * @returns `true` if `T` is exactly `bigint`, otherwise `false`. * @example * ````ts IsBigInt<unknown | bigint>; // false IsBigInt<symbol | Nullable>; // false IsBigInt<bigint>; // true * ```` */ type IsExactlyBigInt<T> = Equals<T, bigint>; /** * @returns `true` if `T` is `symbol`, otherwise `false`. */ type IsSymbol<T> = T extends symbol ? true : false; /** * @returns `true` if `T` is exactly `symbol`, otherwise `false`. * @example * ````ts IsExactlySymbol<symbol>; // true IsExactlySymbol<any | symbol>; // false IsExactlySymbol<Integer<2>>; // false * ```` */ type IsExactlySymbol<T> = Equals<T, symbol>; /** * If ``T`` is exactly``any``, return ``true``, otherwise, return ``false``. * @example * ````ts IsExactlyAny<any | Nullable>; // true, once unsafe, always unsafe IsExactlyAny<Numeric | Nullable>; // false IsExactlyAny<unknown>; // false IsExactlyAny<any>, true * ```` */ type IsExactlyAny<T> = Equals<T, any>; /** * @returns `true` if `T` is a `Newable`, otherwise `false`. */ type IsNewable<T> = IfExtends<T, Newable, true, false>; /** * A type that excludes `null` and `undefined` from type `T`. * @example * Type Str = ExcludeNullable<string | null> // string * Type Str2 = ExcludeNullable<string | null | undefined> // string * Type Str3 = ExcludeNullable<string | undefined> // string * */ type ExcludeNullable<T> = Exclude<T, Nullable>; /** * A type that excludes `undefined` from type `T`. * @example * Type Str = ExcludeNullable<string | undefined> // Result: string */ type ExcludeUndefined<T> = Exclude<T, undefined>; /** * A type that excludes `null` from type `T`. * @example * Type Str = ExcludeNullable<string | null> // Result: string */ type ExcludeNull<T> = Exclude<T, null>; /** * Evaluates whether one type `T` is assignable to another type `U`. * @returns `true` if `T` is assignable to `U`, `false` otherwise. * * @example * ```typescript * type A = { x: number }; * type B = { x: number; y: string }; * type C = { x: number; y?: string }; * * type Check1 = Extends<A, B>; // false, A does not extend B * type Check2 = Extends<B, A>; // true, B extends A * type Check3 = Extends<C, B>; // true, C extends B * ``` */ type Extends<T, U> = T extends never ? false : T extends U ? true : false; /** * `Simplify<T>` flattens the structure of a given type by resolving intersections * and reducing redundant wrapping, making complex types easier to work with. * This type is particularly helpful for deeply nested mapped types, where * readability and simplicity of the resulting type is crucial. * * @template T - The type to simplify. * * @example * ```ts * type Flattened = Simplify<{ a: string } & { b: number }>; // Result: { a: string; b: number } * ``` */ type Simplify<T> = { [KeyType in Keys<T>]: T[KeyType]; } & EmptyObject; /** * `PartialExcept<T, K extends keyof T>` is a utility type that makes all properties of `T` optional * except for the properties specified in `K`, which are required. This is useful for scenarios * where you want to enforce that certain fields must be present while allowing others to be omitted. * * @template T - The original type from which to derive the new type. * @template K - A subset of keys from `T` that should remain required in the resulting type. * * @example * ```ts * type User = { * id: number; * name: string; * email: string; * }; * * type UserUpdate = PartialExcept<User, 'email'>; // Result: { id?: number; name?: string; email: string; } * ``` */ type PartialExcept<T, K extends keyof T> = { [P in K]: T[P]; } & Partial<Omit<T, K>>; /** * Recursively resolves all nested `Promise` types to their underlying value. * Useful when dealing with complex, deeply nested promise chains. * * @example * type A = Promise<Promise<Promise<string>>>; * type B = DeepAwaited<A>; // Result: string */ type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T; type KeysOfUnion$1<T> = T extends T ? keyof T : never; /** * `ExclusiveUnion<T>` creates a union type where each member has its own properties as required, * while properties from other members of the union are made optional and set to `undefined`. * This is useful for cases where different configurations or variants in a union require only their specific fields. * * @template T - The union of object types for which partially optionalized variants should be created. * @template AllKeys - The union of all possible keys across the union's types, derived from `KeysOfUnion`. * * @example * ```ts * type Config = ExclusiveUnion< * | { dbConnectionString: string; maxConnections: number } * | { apiEndpoint: string; apiKey: string } * | { storageBucket: string; accessKeyId: string; secretAccessKey: string } * >; * * // Example usage: * function configureService(config: Config) { * if (config.dbConnectionString) { * console.log(`Configuring database with connection string ${config.dbConnectionString}`); * } else if (config.apiEndpoint) { * console.log(`Configuring API with endpoint ${config.apiEndpoint}`); * } else if (config.storageBucket && config.accessKeyId && config.secretAccessKey) { * console.log(`Configuring storage bucket ${config.storageBucket}`); * } else { * console.log('Invalid configuration'); * } * } * * configureService({ dbConnectionString: 'postgres://...', maxConnections: 100 }); * configureService({ apiEndpoint: 'https://api.example.com', apiKey: '1234' }); * configureService({ storageBucket: 'my-bucket', accessKeyId: 'AKIA...', secretAccessKey: 'abcd' }); * ``` */ type ExclusiveUnion<T extends object, AllKeys extends KeysOfUnion$1<T> = KeysOfUnion$1<T>> = Simplify<T extends unknown ? T & Partial<Record<Exclude<AllKeys, Keys<T>>, undefined>> : never>; /** * `KeysOfUnion` extracts the union of keys from a given union of object types. * This is useful in scenarios where you need to access all possible keys across * unioned object types within conditional or mapped types. * * @template T - The union of object types to extract keys from. * * @example * ```ts * type UnionKeys = KeysOfUnion<{ a: string } | { b: number }>; // Result: 'a' | 'b' * ``` */ type KeysOfUnion<T> = T extends T ? keyof T : never; /** * Turns a given primitive value (except symbol) into its string representation * @example * ```ts * StringifyPrimitive<45> // "45" * StringifyPrimitive<boolean> // "false" | "true" * StringifyPrimitive<null> // "null" * StringifyPrimitive<undefined> // "undefined" * ``` */ type StringifyPrimitive<P extends Exclude<Primitive, symbol>> = `${P}`; /** * Turn a given string literal to a numeric * @example * ````ts` * NumerifyString<'54'>; // 54 * NumerifyString<'699620.000000001'>; // 699620.000000001 * IsNegativeFloat<NumerifyString<'-699620.000000001'>>; // true * ```` */ type NumerifyString$2<S extends string> = S extends `${infer N extends Numeric}` ? N : never; /** * Checks if a given numeric value is in ]-∞,0[ * @returns * true if it is, otherwise false */ type IsNegative<N extends Numeric> = StringifyPrimitive<N> extends `-${infer U}` ? true : false; /** * Checks if a given numeric value is in [0,+∞[ * @returns * true if it is, otherwise false */ type IsPositive<N extends Numeric> = N extends N ? Numeric extends N ? boolean : `${N}` extends `-${Numeric}` ? false : true : never; /** * Check if a given numeric value is an integer * @returns * true if it is, else false */ type IsInteger<N extends Numeric> = number extends N ? false | true : N extends N ? `${N}` extends `${string}.${string}` ? false : true : never; /** * Check if a given numeric value is an float * @returns * true if it is, else false */ type IsFloat<N extends Numeric> = number extends N ? false | true : N extends N ? `${N}` extends `${string}.${string}` ? true : false : never; /** * Get the absolute value of a numeric N * @example * ```ts * Abs<-54>; // Result: 54 * Abs<54>; // Result: 54 * ``` * @returns * |N| */ type Abs<N extends Numeric> = `${N}` extends `-${infer M extends Numeric}` ? M : N; /** * Represents an integer type. * This type is used to ensure that a numeric value is an integer. * * Example use case: * * ```ts * export function myFunc<T extends Numeric>(a: Integer<T>) { * console.log(a); * } * const good = myFunc(4545); // This is valid as 4545 is an integer. * const bad = myFunc(4545.554); // This will throw an error as 4545.554 is not an integer. * ``` */ type Integer<N extends Numeric> = IfEquals<IsInteger<N>, true, N, never>; /** * Type representing an integer that's in [0,+∞[ */ type PositiveInteger<N extends Numeric> = IfEquals<IsPositiveInteger<N>, true, Integer<N>, never>; /** * Represents a positive integer parsed from a string. * If the string does not represent a positive integer, it resolves to `never`, else * it resolves to its integer representation. * @example * ````ts PositiveIntegerString<'0'>; // works PositiveIntegerString<'82739283293237'>; // works PositiveIntegerString<'82739.283293237'>; // never PositiveIntegerString<'-82739.283293237'>; // never PositiveIntegerString<'-1'>; // never * ```` */ type PositiveIntegerString<S extends string> = IfEquals<IsPositiveInteger<Integer<NumerifyString$1<S>>>, true, Integer<NumerifyString$1<S>>, never>; /** * Type representing an integer that's in ]-∞, 0[ */ type NegativeInteger<N extends Numeric> = IfEquals<IsNegativeInteger<N>, true, Integer<N>, never>; /** * Represents a negative integer parsed from a string. * If the string does not represent a negative integer, it resolves to `never`, else * it resolves to its integer representation. * @example * ````ts NegativeIntegerString<'0'>; // never NegativeIntegerString<'82739283293237'>; // never NegativeIntegerString<'-82739.283293237'>; // works NegativeIntegerString<'-82739.283293237'>; // never NegativeIntegerString<'-1'>; // works * ```` */ type NegativeIntegerString<S extends string> = IfEquals<IsNegativeInteger<Integer<NumerifyString$1<S>>>, true, Integer<NumerifyString$1<S>>, never>; /** * Is it a negative integer ? * @return * `true` if it is, else `false` */ type IsNegativeInteger<N extends Numeric> = IsNegative<Integer<N>>; /** * Is it a positive integer ? * @return * `true` if it is, else `false` */ type IsPositiveInteger<N extends Numeric> = IsPositive<Integer<N>>; type NumerifyString$1<S extends string> = S extends `${infer N extends Numeric}` ? N : never; /** * Type representing a float */ type Float<N extends Numeric> = IfExtends<IsFloat<N>, true, N, never>; /** * Type representing a float that's in [0,+∞[ */ type PositiveFloat<N extends Numeric> = IfEquals<IsPositiveFloat<N>, true, Float<N>, never>; /** * Represents a positive float parsed from a string. * If the string does not represent a positive float, it resolves to `never`, else * it resolves to its float representation. * @example * ````ts PositiveFloatString<'0'>; // never PositiveFloatString<'82739283293237'>; // works PositiveFloatString<'-82739.283293237'>; // never PositiveFloatString<'-1'>; // never PositiveFloatString<'1.98'>; // works PositiveFloatString<'-1.98'>; // never * ```` */ type PositiveFloatString<S extends string> = IfEquals<IsPositiveFloat<Float<NumerifyString<S>>>, true, Float<NumerifyString<S>>, never>; /** * Type representing a float that's in ]-∞, 0[ */ type NegativeFloat<N extends Numeric> = IfEquals<IsNegativeFloat<N>, true, Float<N>, never>; /** * Represents a negative float parsed from a string. * If the string does not represent a negative float, it resolves to `never`, else * it resolves to its float representation. * @example * ````ts NegativeFloatString<'0'>; // never NegativeFloatString<'82739283293237'>; // never NegativeFloatString<'-82739.283293237'>; // works NegativeFloatString<'-1'>; // never NegativeFloatString<'-1.98'>; // works * ```` */ type NegativeFloatString<S extends string> = IfEquals<IsNegativeFloat<Float<NumerifyString<S>>>, true, Float<NumerifyString<S>>, never>; /** * Is it a negative float ? * @return * `true` if it is, else `false` */ type IsNegativeFloat<N extends Numeric> = IsNegative<Float<N>>; /** * Is it a positive float ? * @return * `true` if it is, else `false` */ type IsPositiveFloat<N extends Numeric> = IsPositive<Float<N>>; type NumerifyString<S extends string> = S extends `${infer N extends Numeric}` ? N : never; /** * Represents an odd numeric? * @example * ````ts * Odd<2587967>; // 2587967 * Odd<215848141>; // 215848141 * Odd<200000000000000>; // never * Odd<200000000000000.55>; // never * Odd<200000000000001.53>; // never * ```` */ type Odd<T extends Numeric> = IfExtends<StringifyPrimitive<Integer<T>>, `${Numeric | ''}${1 | 3 | 5 | 7 | 9}`, T, never>; /** * Represents an even numeric * @example * ````ts * Even<200000000000000>; // 258796 * Even<258796>; // 258796 * Even<2000000000000001>; // never * ```` */ type Even<T extends Numeric> = IfExtends<StringifyPrimitive<Integer<T>>, `${Numeric | ''}${2 | 4 | 6 | 8 | 0}`, T, never>; /** * A type mapping interface that enables incremental type-level arithmetic operations. * This interface maps each number from 0 to 32,767 to its successor (n + 1), * enabling compile-time numeric operations in TypeScript's type system. * * @remarks * This interface is primarily used as a helper for other type utilities that need * to perform incremental operations, such as the `PositiveRange` type utility. * The mapping is limited to numbers up to 32,768 due to TypeScript's computational limits. * * @example * ```typescript * type Next = NumberMap[5]; // Results in: 6 * type Invalid = NumberMap[32769]; // Results in: never * ``` * * @internal * This interface is not intended to be used directly by consumers of the library. */ interface NumberMap { 0: 1; 1: 2; 2: 3; 3: 4; 4: 5; 5: 6; 6: 7; 7: 8; 8: 9; 9: 10; 10: 11; 11: 12; 12: 13; 13: 14; 14: 15; 15: 16; 16: 17; 17: 18; 18: 19; 19: 20; 20: 21; 21: 22; 22: 23; 23: 24; 24: 25; 25: 26; 26: 27; 27: 28; 28: 29; 29: 30; 30: 31; 31: 32; 32: 33; 33: 34; 34: 35; 35: 36; 36: 37; 37: 38; 38: 39; 39: 40; 40: 41; 41: 42; 42: 43; 43: 44; 44: 45; 45: 46; 46: 47; 47: 48; 48: 49; 49: 50; 50: 51; 51: 52; 52: 53; 53: 54; 54: 55; 55: 56; 56: 57; 57: 58; 58: 59; 59: 60; 60: 61; 61: 62; 62: 63; 63: 64; 64: 65; 65: 66; 66: 67; 67: 68; 68: 69; 69: 70; 70: 71; 71: 72; 72: 73; 73: 74; 74: 75; 75: 76; 76: 77; 77: 78; 78: 79; 79: 80; 80: 81; 81: 82; 82: 83; 83: 84; 84: 85; 85: 86; 86: 87; 87: 88; 88: 89; 89: 90; 90: 91; 91: 92; 92: 93; 93: 94; 94: 95; 95: 96; 96: 97; 97: 98; 98: 99; 99: 100; 100: 101; 101: 102; 102: 103; 103: 104; 104: 105; 105: 106; 106: 107; 107: 108; 108: 109; 109: 110; 110: 111; 111: 112; 112: 113; 113: 114; 114: 115; 115: 116; 116: 117; 117: 118; 118: 119; 119: 120; 120: 121; 121: 122; 122: 123; 123: 124; 124: 125; 125: 126; 126: 127; 127: 128; 128: 129; 129: 130; 130: 131; 131: 132; 132: 133; 133: 134; 134: 135; 135: 136; 136: 137; 137: 138; 138: 139; 139: 140; 140: 141; 141: 142; 142: 143; 143: 144; 144: 145; 145: 146; 146: 147; 147: 148; 148: 149; 149: 150; 150: 151; 151: 152; 152: 153; 153: 154; 154: 155; 155: 156; 156: 157; 157: 158; 158: 159; 159: 160; 160: 161; 161: 162; 162: 163; 163: 164; 164: 165; 165: 166; 166: 167; 167: 168; 168: 169; 169: 170; 170: 171; 171: 172; 172: 173; 173: 174; 174: 175; 175: 176; 176: 177; 177: 178; 178: 179; 179: 180; 180: 181; 181: 182; 182: 183; 183: 184; 184: 185; 185: 186; 186: 187; 187: 188; 188: 189; 189: 190; 190: 191; 191: 192; 192: 193; 193: 194; 194: 195; 195: 196; 196: 197; 197: 198; 198: 199; 199: 200; 200: 201; 201: 202; 202: 203; 203: 204; 204: 205; 205: 206; 206: 207; 207: 208; 208: 209; 209: 210; 210: 211; 211: 212; 212: 213; 213: 214; 214: 215; 215: 216; 216: 217; 217: 218; 218: 219; 219: 220; 220: 221; 221: 222; 222: 223; 223: 224; 224: 225; 225: 226; 226: 227; 227: 228; 228: 229; 229: 230; 230: 231; 231: 232; 232: 233; 233: 234; 234: 235; 235: 236; 236: 237; 237: 238; 238: 239; 239: 240; 240: 241; 241: 242; 242: 243; 243: 244; 244: 245; 245: 246; 246: 247; 247: 248; 248: 249; 249: 250; 250: 251; 251: 252; 252: 253; 253: 254; 254: 255; 255: 256; 256: 257; 257: 258; 258: 259; 259: 260; 260: 261; 261: 262; 262: 263; 263: 264; 264: 265; 265: 266; 266: 267; 267: 268; 268: 269; 269: 270; 270: 271; 271: 272; 272: 273; 273: 274; 274: 275; 275: 276; 276: 277; 277: 278; 278: 279; 279: 280; 280: 281; 281: 282; 282: 283; 283: 284; 284: 285; 285: 286; 286: 287; 287: 288; 288: 289; 289: 290; 290: 291; 291: 292; 292: 293; 293: 294; 294: 295; 295: 296; 296: 297; 297: 298; 298: 299; 299: 300; 300: 301; 301: 302; 302: 303; 303: 304; 304: 305; 305: 306; 306: 307; 307: 308; 308: 309; 309: 310; 310: 311; 311: 312; 312: 313; 313: 314; 314: 315; 315: 316; 316: 317; 317: 318; 318: 319; 319: 320; 320: 321; 321: 322; 322: 323; 323: 324; 324: 325; 325: 326; 326: 327; 327: 328; 328: 329; 329: 330; 330: 331; 331: 332; 332: 333; 333: 334; 334: 335; 335: 336; 336: 337; 337: 338; 338: 339; 339: 340; 340: 341; 341: 342; 342: 343; 343: 344; 344: 345; 345: 346; 346: 347; 347: 348; 348: 349; 349: 350; 350: 351; 351: 352; 352: 353; 353: 354; 354: 355; 355: 356; 356: 357; 357: 358; 358: 359; 359: 360; 360: 361; 361: 362; 362: 363; 363: 364; 364: 365; 365: 366; 366: 367; 367: 368; 368: 369; 369: 370; 370: 371; 371: 372; 372: 373; 373: 374; 374: 375; 375: 376; 376: 377; 377: 378; 378: 379; 379: 380; 380: 381; 381: 382; 382: 383; 383: 384; 384: 385; 385: 386; 386: 387; 387: 388; 388: 389; 389: 390; 390: 391; 391: 392; 392: 393; 393: 394; 394: 395; 395: 396; 396: 397; 397: 398; 398: 399; 399: 400; 400: 401; 401: 402; 402: 403; 403: 404; 404: 405; 405: 406; 406: 407; 407: 408; 408: 409; 409: 410; 410: 411; 411: 412; 412: 413; 413: 414; 414: 415; 415: 416; 416: 417; 417: 418; 418: 419; 419: 420; 420: 421; 421: 422; 422: 423; 423: 424; 424: 425; 425: 426; 426: 427; 427: 428; 428: 429; 429: 430; 430: 431; 431: 432; 432: 433; 433: 434; 434: 435; 435: 436; 436: 437; 437: 438; 438: 439; 439: 440; 440: 441; 441: 442; 442: 443; 443: 444; 444: 445; 445: 446; 446: 447; 447: 448; 448: 449; 449: 450; 450: 451; 451: 452; 452: 453; 453: 454; 454: 455; 455: 456; 456: 457; 457: 458; 458: 459; 459: 460; 460: 461; 461: 462; 462: 463; 463: 464; 464: 465; 465: 466; 466: 467; 467: 468; 468: 469; 469: 470; 470: 471; 471: 472; 472: 473; 473: 474; 474: 475; 475: 476; 476: 477; 477: 478; 478: 479; 479: 480; 480: 481; 481: 482; 482: 483; 483: 484; 484: 485; 485: 486; 486: 487; 487: 488; 488: 489; 489: 490; 490: 491; 491: 492; 492: 493; 493: 494; 494: 495; 495: 496; 496: 497; 497: 498; 498: 499; 499: 500; 500: 501; 501: 502; 502: 503; 503: 504; 504: 505; 505: 506; 506: 507; 507: 508; 508: 509; 509: 510; 510: 511; 511: 512; 512: 513; 513: 514; 514: 515; 515: 516; 516: 517; 517: 518; 518: 519; 519: 520; 520: 521; 521: 522; 522: 523; 523: 524; 524: 525; 525: 526; 526: 527; 527: 528; 528: 529; 529: 530; 530: 531; 531: 532; 532: 533; 533: 534; 534: 535; 535: 536; 536: 537; 537: 538; 538: 539; 539: 540; 540: 541; 541: 542; 542: 543; 543: 544; 544: 545; 545: 546; 546: 547; 547: 548; 548: 549; 549: 550; 550: 551; 551: 552; 552: 553; 553: 554; 554: 555; 555: 556; 556: 557; 557: 558; 558: 559; 559: 560; 560: 561; 561: 562; 562: 563; 563: 564; 564: 565; 565: 566; 566: 567; 567: 568; 568: 569; 569: 570; 570: 571; 571: 572; 572: 573; 573: 574; 574: 575; 575: 576; 576: 577; 577: 578; 578: 579; 579: 580; 580: 581; 581: 582; 582: 583; 583: 584; 584: 585; 585: 586; 586: 587; 587: 588; 588: 589; 589: 590; 590: 591; 591: 592; 592: 593; 593: 594; 594: 595; 595: 596; 596: 597; 597: 598; 598: 599; 599: 600; 600: 601; 601: 602; 602: 603; 603: 604; 604: 605; 605: 606; 606: 607; 607: 608; 608: 609; 609: 610; 610: 611; 611: 612; 612: 613; 613: 614; 614: 615; 615: 616; 616: 617; 617: 618; 618: 619; 619: 620; 620: 621; 621: 622; 622: 623; 623: 624; 624: 625; 625: 626; 626: 627; 627: 628; 628: 629; 629: 630; 630: 631; 631: 632; 632: 633; 633: 634; 634: 635; 635: 636; 636: 637; 637: 638; 638: 639; 639: 640; 640: 641; 641: 642; 642: 643; 643: 644; 644: 645; 645: 646; 646: 647; 647: 648; 648: 649; 649: 650; 650: 651; 651: 652; 652: 653; 653: 654; 654: 655; 655: 656; 656: 657; 657: 658; 658: 659; 659: 660; 660: 661; 661: 662; 662: 663; 663: 664; 664: 665; 665: 666; 666: 667; 667: 668; 668: 669; 669: 670; 670: 671; 671: 672; 672: 673; 673: 674; 674: 675; 675: 676; 676: 677; 677: 678; 678: 679; 679: 680; 680: 681; 681: 682; 682: 683; 683: 684; 684: 685; 685: 686; 686: 687; 687: 688; 688: 689; 689: 690; 690: 691; 691: 692; 692: 693; 693: 694; 694: 695; 695: 696; 696: 697; 697: 698; 698: 699; 699: 700; 700: 701; 701: 702; 702: 703; 703: 704; 704: 705; 705: 706; 706: 707; 707: 708; 708: 709; 709: 710; 710: 711; 711: 712; 712: 713; 713: 714; 714: 715; 715: 716; 716: 717; 717: 718; 718: 719; 719: 720; 720: 721; 721: 722; 722: 723; 723: 724; 724: 725; 725: 726; 726: 727; 727: 728; 728: 729; 729: 730; 730: 731; 731: 732; 732: 733; 733: 734; 734: 735; 735: 736; 736: 737; 737: 738; 738: 739; 739: 740; 740: 741; 741: 742; 742: 743; 743: 744; 744: 745; 745: 746; 746: 747; 747: 748; 748: 749; 749: 750; 750: 751; 751: 752; 752: 753; 753: 754; 754: 755; 755: 756; 756: 757; 757: 758; 758: 759; 759: 760; 760: 761; 761: 762; 762: 763; 763: 764; 764: 765; 765: 766; 766: 767; 767: 768; 768: 769; 769: 770; 770: 771; 771: 772; 772: 773; 773: 774; 774: 775; 775: 776; 776: 777; 777: 778; 778: 779; 779: 780; 780: 781; 781: 782; 782: 783; 783: 784; 784: 785; 785: 786; 786: 787; 787: 788; 788: 789; 789: 790; 790: 791; 791: 792; 792: 793; 793: 794; 794: 795; 795: 796; 796: 797; 797: 798; 798: 799; 799: 800; 800: 801; 801: 802; 802: 803; 803: 804; 804: 805; 805: 806; 806: 807; 807: 808; 808: 809; 809: 810; 810: 811; 811: 812; 812: 813; 813: 814; 814: 815; 815: 816; 816: 817; 817: 818; 818: 819; 819: 820; 820: 821; 821: 822; 822: 823; 823: 824; 824: 825; 825: 826; 826: 827; 827: 828; 828: 829; 829: 830; 830: 831; 831: 832; 832: 833; 833: 834; 834: 835; 835: 836; 836: 837; 837: 838; 838: 839; 839: 840; 840: 841; 841: 842; 842: 843; 843: 844; 844: 845; 845: 846; 846: 847; 847: 848; 848: 849; 849: 850; 850: 851; 851: 852; 852: 853; 853: 854; 854: 855; 855: 856; 856: 857; 857: 858; 858: 859; 859: 860; 860: 861; 861: 862; 862: 863; 863: 864; 864: 865; 865: 866; 866: 867; 867: 868; 868: 869; 869: 870; 870: 871; 871: 872; 872: 873; 873: 874; 874: 875; 875: 876; 876: 877; 877: 878; 878: 879; 879: 880; 880: 881; 881: 882; 882: 883; 883: 884; 884: 885; 885: 886; 886: 887; 887: 888; 888: 889; 889: 890; 890: 891; 891: 892; 892: 893; 893: 894; 894: 895; 895: 896; 896: 897; 897: 898; 898: 899; 899: 900; 900: 901; 901: 902; 902: 903; 903: 904; 904: 905; 905: 906; 906: 907; 907: 908; 908: 909; 909: 910; 910: 911; 911: 912; 912: 913; 913: 914; 914: 915; 915: 916; 916: 917; 917: 918; 918: 919; 919: 920; 920: 921; 921: 922; 922: 923; 923: 924; 924: 925; 925: 926; 926: 927; 927: 928; 928: 929; 929: 930; 930: 931; 931: 932; 932: 933; 933: 934; 934: 935; 935: 936; 936: 937; 937: 938; 938: 939; 939: 940; 940: 941; 941: 942; 942: 943; 943: 944; 944: 945; 945: 946; 946: 947; 947: 948; 948: 949; 949: 950; 950: 951; 951: 952; 952: 953; 953: 954; 954: 955; 955: 956; 956: 957; 957: 958; 958: 959; 959: 960; 960: 961; 961: 962; 962: 963; 963: 964; 964: 965; 965: 966; 966: 967; 967: 968; 968: 969; 969: 970; 970: 971; 971: 972; 972: 973; 973: 974; 974: 975; 975: 976; 976: 977; 977: 978; 978: 979; 979: 980; 980: 981; 981: 982; 982: 983; 983: 984; 984: 985; 985: 986; 986: 987; 987: 988; 988: 989; 989: 990; 990: 991; 991: 992; 992: 993; 993: 994; 994: 995; 995: 996; 996: 997; 997: 998; 998: 999; } /** * Represents a range of positive integers from N to M (inclusive). * Both bounds must be positive integers. * * @template N Lower bound (must be positive integer) * @template M Upper bound (must be positive integer) * * @example * ```typescript * // Pagination example with a maximum of 100 items per page * interface PaginationParams { * page: number; * itemsPerPage: PositiveRange<1, 100>; * } * * async function fetchUsers({ page, itemsPerPage }: PaginationParams) { * const offset = (page - 1) * itemsPerPage; * return await db.users.findMany({ * skip: offset, * take: itemsPerPage, * }); * } * * // Valid usage: * fetchUsers({ page: 1, itemsPerPage: 50 }); // ✅ OK * * // Type errors: * fetchUsers({ page: 1, itemsPerPage: 200 }); // ❌ Error: 200 exceeds maximum of 100 * fetchUsers({ page: 1, itemsPerPage: 0 }); // ❌ Error: 0 is not in range * ``` */ type PositiveRange<N extends number, M extends number> = [ IsPositiveInteger<N>, IsPositiveInteger<M> ] extends [true, true] ? N extends M ? N : M extends N ? M : number extends N | M ? number : _BuildRange<N, M, []> : never; /** * @hidden * @Internal helper to build range union recursively */ type _BuildRange<N extends number, M extends number, Acc extends number[], Current extends number = N> = Current extends M ? Current | Acc[number] : _BuildRange<N, M, [...Acc, Current], AddOne<Current>>; /** * @hidden * @internal */ type AddOne<N extends number> = N extends keyof NumberMap ? NumberMap[N] : never; type EmptyArray = []; /** * Get the length of a given string * @example * ```ts * Strlen<'foo'>; // Result: 3 * ``` * * Can be used to create type constraints on string lengths: * ```ts * // Only allow strings of length 8-10 characters * type ValidPassword<T extends string> = Strlen<T> extends 8 | 9 | 10 ? T : never; * * // Function that only accepts valid password strings * function checkValidPassword<T extends string>(val: T & ValidPassword<T>) { * console.log(val); * } * * // Works with string literals * const validPass = checkValidPassword('password123' as const); // OK * const invalidPass = checkValidPassword('short' as const); // Type error * ``` */ type Strlen<S extends string, Arr extends unknown[] = EmptyArray> = S extends `${infer L}${infer R}` ? Strlen<R, [...Arr, L]> : Arr['length']; /** * Check if a string is non-empty * @example * ```ts * export function filledString<S extends string>(s: FilledString<S>) { * return s; * } * console.log(filledString('hello')); // Ok * console.log(filledString('')); // Error * ``` */ type FilledString<T extends string = string> = Strlen<T> extends 0 ? Message<'must be non-empty string'> : T; /** * Check if two strings S1 and S2 have the same length * @returns * ``true`` if they do, else ``false`` */ type EqualStrlen<S1 extends string, S2 extends string> = Equals<Strlen<S1>, Strlen<S2>>; /** * internal numeric tuple builder * converts a number literal to a tuple of that length for type-level math. * this is not the same as your Tuple<T> or SizedTuple<T, N> types; * it exists solely for numeric reasoning (e.g. comparisons like A <= B) */ type BuildTupleFromNumber<N extends number, T extends unknown[] = []> = T['length'] extends N ? T : BuildTupleFromNumber<N, [...T, unknown]>; /** * type-level numeric comparator * evaluates to true if A <= B, else false */ type Lte<A extends number, B extends number> = BuildTupleFromNumber<B> extends [ ...BuildTupleFromNumber<A>, ...unknown[] ] ? true : false; /** * Constrain string to have length <= Max */ type StrMax<T extends string, Max extends number> = Lte<Strlen<T>, Max> extends true ? T : Message<`must be at most ${Max} characters long`>; /** * Constrain string to have length >= Min */ type StrMin<T extends string, Min extends number> = Lte<Min, Strlen<T>> extends true ? T : Message<`must be at least ${Min} characters long`>; /** * Constrain string to have Min <= length <= Max */ type StrBetween<T extends string, Min extends number, Max extends number> = Lte<Min, Strlen<T>> extends true ? Lte<Strlen<T>, Max> extends true ? T : Message<`must be at most ${Max} characters long`> : Message<`must be at least ${Min} characters long`>; /** * EnforcedString * * Constructive constraint builder: * - Prefix: must start with this * - Contains: must contain this anywhere * - Suffix: must end with this * * If a knob is the wide `string`, that knob imposes no constraint. * If all three are `string`, the result is plain `string`. * * @examples * type A = EnforcedString<'pk_'>; // `pk_${string}` * type B = EnforcedString<string, 'ABC'>; // `${string}ABC${string}` * type C = EnforcedString<string, string, '.json'>; // `${string}.json` * type D = EnforcedString<'pk_', 'ABC', '.json'>; // `pk_${string}ABC${string}.json` * type E = EnforcedString<string, string, string>; // string */ type EnforcedString<Prefix extends string = string, Contains extends string = string, Suffix extends string = string> = Equals<Prefix, string> extends true ? Equals<Contains, string> extends true ? Equals<Suffix, string> extends true ? string : `${string}${Suffix}` : Equals<Suffix, string> extends true ? `${string}${Contains}${string}` : `${string}${Contains}${string}${Suffix}` : Equals<Contains, string> extends true ? Equals<Suffix, string> extends true ? `${Prefix}${string}` : `${Prefix}${string}${Suffix}` : Equals<Suffix, string> extends true ? `${Prefix}${string}${Contains}${string}` : `${Prefix}${string}${Contains}${string}${Suffix}`; /** * Check if a string starts with another string * @example * ```ts * type Result = StringStartsWith<'hello world', 'hello'>; // Result: true * ``` */ type StringStartsWith<T extends string, U extends string> = IfExtends<T, `${U}${string}`, true, false>; /** * Check if a string ends with another string * @example * ```ts * type Result = StringEndsWith<'hello world', 'world'>; // Result: true * ``` */ type StringEndsWith<T extends string, U extends string> = IfExtends<T, `${string}${U}`, true, false>; /** * Capitalizes the first character of a string literal type while preserving the rest. * This is particularly useful when you need to transform string literal unions into * their capitalized counterparts. * * @template T The string literal type to capitalize * * @example * ```ts * type Category = "software" | "health" | "philosophy"; * type Result = CapitalizeFirst<Category>; // "Software" | "Health" | "Philosophy" * * type Single = CapitalizeFirst<"hello">; // "Hello" * ``` */ type CapitalizeFirst<T extends string> = T extends `${infer First}${infer Rest}` ? `${Capitalize<First>}${Rest}` : T; /** * Represents a tuple. * @example * ````ts * Tuple<['a', 'b']>; //