ts-roids
Version:
Bullet-proof TS even more
2,005 lines (1,985 loc) • 82.4 kB
TypeScript
/**
* 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']>; //