/** * Prints custom error message * * @param messages - Error message * @returns Custom error message * * @example * type Result = Error<'Custom error message'> * // ^? type Result = ['Error: Custom error message'] */ export type Error = messages extends string ? [ // Surrounding with array to prevent `messages` from being widened to `string` `Error: ${messages}`, ] : { [key in keyof messages]: messages[key] extends infer message extends string ? `Error: ${message}` : never } /** * Filters out all members of {@link items} that are {@link item} * * @param items - Items to filter * @param item - Type to filter out * @returns Filtered items * * @example * type Result = Filter<['a', 'b', 'c'], 'b'> * // ^? type Result = ['a', 'c'] */ export type Filter< items extends readonly unknown[], item, /// acc extends readonly unknown[] = [], > = items extends readonly [ infer head, ...infer tail extends readonly unknown[], ] ? [head] extends [item] ? Filter : Filter : readonly [...acc] /** * Checks if {@link type} can be narrowed further than {@link type2} * * @param type - Type to check * @param type2 - Type to against * * @example * type Result = IsNarrowable<'foo', string> * // ^? true */ export type IsNarrowable = IsUnknown extends true ? false : IsNever< (type extends type2 ? true : false) & (type2 extends type ? false : true) > extends true ? false : true /** * Checks if {@link type} is `never` * * @param type - Type to check * * @example * type Result = IsNever * // ^? type Result = true */ export type IsNever = [type] extends [never] ? true : false /** * Checks if {@link type} is `unknown` * * @param type - Type to check * @returns `true` if {@link type} is `unknown`, otherwise `false` * * @example * type Result = IsUnknown * // ^? type Result = true */ export type IsUnknown = unknown extends type ? true : false /** * Joins array into string * * @param array - Array to join * @param separator - Separator * @returns string * * @example * type Result = Join<['a', 'b', 'c'], '-'> * // ^? type Result = 'a-b-c' */ export type Join< array extends readonly unknown[], separator extends string | number, > = array extends readonly [infer head, ...infer tail] ? tail['length'] extends 0 ? `${head & string}` : `${head & string}${separator}${Join}` : never /** * Merges two object types into new type * * @param object1 - Object to merge into * @param object2 - Object to merge and override keys from {@link object1} * @returns New object type with keys from {@link object1} and {@link object2}. If a key exists in both {@link object1} and {@link object2}, the key from {@link object2} will be used. * * @example * type Result = Merge<{ foo: string }, { foo: number; bar: string }> * // ^? type Result = { foo: number; bar: string } */ export type Merge = Omit & object2 /** * Makes objects destructurable. * * @param union - Union to distribute. * * @example * type Result = OneOf<{ foo: boolean } | { bar: boolean }> * // ^? type Result = { foo: boolean; bar?: undefined; } | { bar: boolean; foo?: undefined; } */ export type OneOf< union extends object, /// allKeys extends KeyofUnion = KeyofUnion, > = union extends infer item ? Pretty]?: never }> : never type KeyofUnion = type extends type ? keyof type : never /** * Combines members of an intersection into a readable type. * * @link https://twitter.com/mattpocockuk/status/1622730173446557697?s=20&t=NdpAcmEFXY01xkqU3KO0Mg * @example * type Result = Pretty<{ a: string } | { b: string } | { c: number, d: bigint }> * // ^? type Result = { a: string; b: string; c: number; d: bigint } */ export type Pretty = { [key in keyof type]: type[key] } & unknown /** * Creates range between two positive numbers using [tail recursion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html#tail-recursion-elimination-on-conditional-types). * * @param start - Number to start range * @param stop - Number to end range * @returns Array with inclusive range from {@link start} to {@link stop} * * @example * type Result = Range<1, 3> * // ^? type Result = [1, 2, 3] */ // From [Type Challenges](https://github.com/type-challenges/type-challenges/issues/11625) export type Range< start extends number, stop extends number, /// result extends number[] = [], padding extends 0[] = [], current extends number = [...padding, ...result]['length'] & number, > = current extends stop ? current extends start ? [current] : result extends [] ? [] : [...result, current] : current extends start ? Range : result extends [] ? Range : Range /** * Trims empty space from type {@link t}. * * @param t - Type to trim * @param chars - Characters to trim * @returns Trimmed type * * @example * type Result = Trim<' foo '> * // ^? type Result = "foo" */ export type Trim = TrimLeft< TrimRight, chars > type TrimLeft = t extends `${chars}${infer tail}` ? TrimLeft : t type TrimRight< t, chars extends string = ' ', > = t extends `${infer head}${chars}` ? TrimRight : t /** * Create tuple of {@link type} type with {@link size} size * * @param Type - Type of tuple * @param Size - Size of tuple * @returns Tuple of {@link type} type with {@link size} size * * @example * type Result = Tuple * // ^? type Result = [string, string] */ // https://github.com/Microsoft/TypeScript/issues/26223#issuecomment-674500430 export type Tuple = size extends size ? number extends size ? type[] : _TupleOf : never type _TupleOf< length, size extends number, acc extends readonly unknown[], > = acc['length'] extends size ? acc : _TupleOf