UNPKG

flat-type-samurai

Version:

Advanced utility types for Typescript

1,933 lines (1,782 loc) 94.8 kB
/** * Returns the second argument if the first argument is `true` (defaults to `true`), otherwise returns the third argument (defaults to `false`) * ```ts * // valid * type Case1 = If<true, 'valid'> * // invalid * type Case2 = If<false, 'valid', 'invalid'> * ``` */ type If< Condition, IfTrue = true, IfFalse = false > = Condition extends true ? IfTrue : IfFalse; /** * Returns a boolean if the passed type is `never` * @example * ```ts * // true * type Case1 = IsNever<never> * // false * type Case2 = IsNever<true> * ``` */ type IsNever<T> = [T] extends [never] ? true : false; /** * Returns the second argument if the first argument is `never` (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // 'valid' * type Case1 = IfNever<never, 'valid'> * // 'invalid' * type Case2 = IfNever<never, 'valid', 'invalid'> * ``` */ type IfNever<T, IfTrue = true, IfFalse = false> = If< IsNever<T>, IfTrue, IfFalse >; type NeverifyPropertiesOptions = { makeOptional: boolean; }; /** * Turns all properties of an object to type `unknown`. * If `makeOptional` option is `true`, makes all properties optional * @example * ```ts * // {a:never; b:never} * type Result = NeverifyProperties<{a: string; b: string}> * ``` */ type NeverifyProperties< T extends object, Options extends NeverifyPropertiesOptions = { makeOptional: false } > = { [K in keyof T]: never; } extends infer Result ? If<Options["makeOptional"], Partial<Result>, Result> : never; /** * Returns the first argument if it is an empty array, otherwise returns `never` * @example * ```ts * // never * type Result = EmptyArray<[1]> * ``` */ type EmptyArray<T extends readonly unknown[]> = T extends readonly [ unknown, ...unknown[] ] ? never : T; /** * Returns the first argument if it is a non empty array, otherwise returns `never` * @example * ```ts * // never * type Result = EmptyArray<[1]> * ``` */ type NonEmptyArray<T extends readonly unknown[]> = If< IsNever<EmptyArray<T>>, T, never >; /** * Returns a boolean whether the passed argument is an empty array * @example * ```ts * // false * type Result - IsEmptyArray<[1]> */ type IsEmptyArray<T extends readonly unknown[]> = If< IsNever<EmptyArray<T>>, false, true >; /** * Returns a boolean whether the passed argument is a non empty array * @example * ```ts * // true * type Result - IsEmptyArray<[1]> */ type IsNonEmptyArray<T extends readonly unknown[]> = If< IsNever<EmptyArray<T>>, true, false >; /** * Returns the second argument if the first argument is an empty array (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // string * type Result = IfEmptyArray<[], string, number> * ``` */ type IfEmptyArray< T extends readonly unknown[], IfTrue = true, IfFalse = false > = If<IsEmptyArray<T>, IfTrue, IfFalse>; /** * Returns the second argument if the first argument is a non empty array (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // string * type Result = IfEmptyArray<[1], string, number> * ``` */ type IfNonEmptyArray< T extends readonly unknown[], IfTrue = true, IfFalse = false > = If<IsNonEmptyArray<T>, IfTrue, IfFalse>; /** * Accepts a boolean and returns `true` if the passed type is `false`, otherwise returns `true` * @example * ```ts * // false * type Case1 = Not<true> * // true * type Case2 = Not<false> * ``` */ type Not<T extends boolean> = T extends true ? false : true; type PopOptions = { includeRemoved: boolean; }; /** * Removes last element from the first array argument. * If the `includeRemoved` option is `true` return removed element with the new array in the format of [rest, removed] * @example * ```ts * // [1, 2] * type Case1 = Pop<[1, 2, 3]> * // [[1, 2], 3] * type Case2 = Pop<[1, 2, 3], {includeRemoved: true}> * ``` */ type Pop< T extends readonly unknown[], Options extends PopOptions = { includeRemoved: false; } > = IsEmptyArray<T> extends true ? never : T extends readonly [...infer Rest extends readonly unknown[], infer Removed] ? If<Options["includeRemoved"], [Rest, Removed], Rest> : never; /** * Returns boolean whether the first argument extends the second argument * @example * ```ts * // true * type Case1 = Extends<1, number> * // false * type Case2 = Extends<number, 1> * ``` */ type Extends<T, Base> = [T] extends [Base] ? true : false; /** * Returns boolean whether the first argument doesn't extend the second argument * @example * ```ts * // false * type Case1 = Extends<1, number> * // true * type Case2 = Extends<number, 1> * ``` */ type NotExtends<T, Base> = Not<Extends<T, Base>>; /** * Returns the third argument if the first argument extends the second argument (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * @example * ```ts * // 'valid' * type Case1 = IfExtends<1, number, 'valid'> * // 'invalid' * type Case2 = IfExtends<1, string, 'valid', 'invalid'> * ``` */ type IfExtends<T, Base, IfTrue = true, IfFalse = false> = If< Extends<T, Base>, IfTrue, IfFalse >; /** * Returns the third argument if the first argument doesn't extend the second argument (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * @example * ```ts * // 'valid' * type Case1 = IfExtends<1, string, 'valid'> * // 'invalid' * type Case2 = IfExtends<1, number, 'valid', 'invalid'> * ``` */ type IfNotExtends<T, Base, IfTrue = true, IfFalse = false> = If< NotExtends<T, Base>, IfTrue, IfFalse >; /** * Returns boolean whether the every element of first array argument extend the second argument * @example * ```ts * // true * type Case1 = ExtendsArr<[1, 2, 3], number> * // false * type Case1 = ExtendsArr<[1, '2', 3], number> * ``` */ type ExtendsArr< T extends readonly unknown[], Base > = IsEmptyArray<T> extends true ? true : Pop< T, { includeRemoved: true; } > extends readonly [infer Rest extends readonly unknown[], infer Removed] ? Extends<Removed, Base> extends true ? ExtendsArr<Rest, Base> : false : false; /** * Returns result of logical multiplication of two params. * @example * ```ts * // true * type Case1 = And<true, true> * // false * type Case2 = And<false, true> * ``` */ type And<Condition1, Condition2> = IfExtends< Condition1, true, Extends<Condition2, true> >; /** * Returns result of logical multiplication of all elements inside the passed array type * @example * ```ts * // true * type Case1 = And<[true, true, true]> * // false * type Case2 = And<[true, true, false]> * ``` */ type AndArr<Conditions extends readonly unknown[]> = Extends< Conditions[number], true >; /** * Returns a boolean whether the passed argument is literal string * @example * ```ts * // true * type Case1 = IsStringLiteral<'a'> * // false * type Case2 = IsStringLiteral<string> * ``` */ type IsStringLiteral<T extends string> = string extends T ? false : true; /** * Returns the second argument if the first argument is `false` (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // valid * type Case1 = IfNot<false, 'valid'> * // invalid * type Case2 = IfNot<false, 'valid', 'invalid'> * ``` */ type IfNot<Condition, IfTrue = true, IfFalse = false> = If< Condition, IfFalse, IfTrue >; type EmptyString<T extends string> = "" extends T ? string extends T ? never : T : never; type NonEmptyString<T extends string> = string extends T ? string : If<IsNever<EmptyString<T>>, T, never>; type IsEmptyString<T extends string> = IfNot<IsNever<EmptyString<T>>>; type IsNonEmptyString<T extends string> = IfNot< IsNever<NonEmptyString<T>> >; type IfEmptyString< T extends string, IfTrue = true, IfFalse = false > = IfNot<IsNever<EmptyString<T>>, IfTrue, IfFalse>; type IfNonEmptyString< T extends string, IfTrue = true, IfFalse = false > = IfNot<IsNever<NonEmptyString<T>>, IfTrue, IfFalse>; type _AreAnagrams< Str1 extends string, Str2 extends string > = IsEmptyString<Str1> extends true ? IsEmptyString<Str2> extends true ? true : false : Str1 extends `${infer First extends string}${infer Rest1 extends string}` ? Str2 extends `${infer Prev extends string}${First}${infer Rest2 extends string}` ? _AreAnagrams<Rest1, `${Prev}${Rest2}`> : false : never; /** * Returns a boolean whether two passed string literals are anagrams * @example * ```ts * // true * type Case1 = AreAnagrams<"name", "eman"> * // false * type Case1 = AreAnagrams<"name", "emand"> * ``` */ type AreAnagrams<Str1 extends string, Str2 extends string> = And< IsStringLiteral<Str1>, IsStringLiteral<Str2> > extends true ? _AreAnagrams<Str1, Str2> : false; /** * Returns a boolean whether the passed type is `any` * @example * ```ts * // true * type Result = IsAny<any> * ``` */ type IsAny<T> = 0 extends 1 & T ? true : false; /** * Returns the second argument if the first argument is `any` (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // string * type Result = IfAny<any, string, number> * ``` */ type IfAny<T, IfTrue = true, IfFalse = false> = If< IsAny<T>, IfTrue, IfFalse >; type AnifyPropertiesOptions = { makeOptional: boolean; }; /** * Turns all properties of an object to type `any`. * If `makeOptional` option is `true`, makes all properties optional * @example * ```ts * // {a:any; b:any} * type Result = AnifyProperties<{a: string; b: string}> * ``` */ type AnifyProperties< T extends object, Options extends AnifyPropertiesOptions = { makeOptional: false } > = { [K in keyof T]: any; } extends infer Result ? If<Options["makeOptional"], Partial<Result>, Result> : never; /** * Returns the type of the element of the passed array argument * @example * ```ts * // string * type Case1 = ArrayElementType<string[]> * // "a" | "b" * type Case1 = ArrayElementType<readonly ("a" | "b")[]> * ``` */ type ArrayElementType<T extends readonly unknown[]> = T extends Readonly< Array<infer Item> > ? Item : never; type LastCharacterOptions = { includeRest: boolean; }; type _LastCharacter< T extends string, Options extends LastCharacterOptions = { includeRest: false; }, Previous extends string = "" > = string extends T ? string : T extends `${infer First}${infer Rest}` ? IsEmptyString<Rest> extends true ? If<Options["includeRest"], [First, Previous], First> : _LastCharacter<Rest, Options, `${Previous}${First}`> : T; /** * Accepts a string argument and returns its first character. * If the `includeRest` options is `true`, returns the first character and the rest of the string in the format of: [last, rest] * @example * ```ts * // 'c' * type Case1 = LastCharacter<'abc'> * // ['c', 'ab'] * type Case2 = LastCharacter<'abc', {includeRest: true}> * ``` */ type LastCharacter< T extends string, Options extends LastCharacterOptions = { includeRest: false; } > = _LastCharacter<T, Options>; type EvenDigit = "0" | "2" | "4" | "6" | "8"; type Integer<T extends number> = `${T}` extends `${string}.${string}` ? never : T; type Float<T extends number> = If<IsNever<Integer<T>>, T, never>; type Negative<T extends number> = `${T}` extends `-${string}` ? T : never; type Positive<T extends number> = If<IsNever<Negative<T>>, T, never>; type PositiveInteger<T extends number> = Positive<Integer<T>>; type NegativeInteger<T extends number> = Negative<Integer<T>>; type PositiveFloat<T extends number> = Positive<Float<T>>; type NegativeFloat<T extends number> = Negative<Float<T>>; type Even<T extends number> = IfNot< IsNever<Integer<T>>, `${T}` extends `${string}${EvenDigit}` ? T : never, never >; type Odd<T extends number> = IfNot< IsNever<Integer<T>>, If<IsNever<Even<T>>, T, never>, never >; type IsInteger<T extends number> = Not<IsNever<Integer<T>>>; type IsFloat<T extends number> = Not<IsNever<Float<T>>>; type IsEven<T extends number> = If< IsInteger<T>, `${T}` extends `${string}${EvenDigit}` ? true : false >; type IsOdd<T extends number> = If<IsInteger<T>, Not<IsEven<T>>>; type IsPositive<T extends number> = Not<IsNever<Positive<T>>>; type IsNegative<T extends number> = Not<IsNever<Negative<T>>>; type IsPositiveInteger<T extends number> = Not< IsNever<PositiveInteger<T>> >; type IsNegativeInteger<T extends number> = Not< IsNever<NegativeInteger<T>> >; type IsPositiveFloat<T extends number> = Not<IsNever<PositiveFloat<T>>>; type IsNegativeFloat<T extends number> = Not<IsNever<NegativeFloat<T>>>; type IfInteger<T extends number, IfTrue = true, IfFalse = false> = If< IsInteger<T>, IfTrue, IfFalse >; type IfFloat<T extends number, IfTrue = true, IfFalse = false> = If< IsFloat<T>, IfTrue, IfFalse >; type IfEven<T extends number, IfTrue = true, IfFalse = false> = If< IsEven<T>, IfTrue, IfFalse >; type IfOdd<T extends number, IfTrue = true, IfFalse = false> = If< IsOdd<T>, IfTrue, IfFalse >; type IfPositive<T extends number, IfTrue = true, IfFalse = false> = If< IsPositive<T>, IfTrue, IfFalse >; type IfNegative<T extends number, IfTrue = true, IfFalse = false> = If< IsNegative<T>, IfTrue, IfFalse >; type IfPositiveInteger< T extends number, IfTrue = true, IfFalse = false > = If<IsPositiveInteger<T>, IfTrue, IfFalse>; type IfNegativeInteger< T extends number, IfTrue = true, IfFalse = false > = If<IsNegativeInteger<T>, IfTrue, IfFalse>; type IfPositiveFloat< T extends number, IfTrue = true, IfFalse = false > = If<IsPositiveFloat<T>, IfTrue, IfFalse>; type IfNegativeFloat< T extends number, IfTrue = true, IfFalse = false > = If<IsNegativeFloat<T>, IfTrue, IfFalse>; type ParseNumber<T extends string | number> = T extends `${infer NumT extends number}` ? NumT : never; type Abs<T extends number> = `${T}` extends `-${infer PositiveT extends number}` ? PositiveT : T; type Negate<T extends number> = ParseNumber<`-${Abs<T>}`>; /** * Returns a boolean whether the first array argument is fixed length tuple * @example * ```ts * // true * type Case1 = IsTuple<[1, 2, 3]> * // false * type Case2 = IsTuple<number[]> * ``` */ type IsTuple<T extends readonly unknown[]> = NotExtends< number, T["length"] >; /** * Type version of `Array.prototype.join()`. Joins the first array argument by the second argument * @example * ```ts * // 'a-p-p-l-e' * type Case1 = Join<["a", "p", "p", "l", "e"], "-"> * // '21212' * type Case2 = Join<["2", "2", "2"], 1> * // 'o' * type Case3 = Join<["o"], "u"> * ``` */ type Join< T extends readonly (string | number)[], Glue extends string | number > = IsTuple<T> extends true ? T extends readonly [ infer First extends string | number, ...infer Rest extends readonly (string | number)[] ] ? IfEmptyArray<Rest, First, `${First}${Glue}${Join<Rest, Glue>}`> : never : never; /** * Transform numbers, booleans, strings, bigints to string. * ```ts * // 'true' * type Result = Stringify<true> * * ``` */ type Stringify<T> = T extends number | boolean | string | bigint ? `${T}` : never; type DecrementMap = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]; type NegativeCarryMap = { "-1": 9; }; type _Decrement< Number extends string, Result extends string = "" > = Number extends "" ? ParseNumber<Result> : ParseNumber<LastCharacter<Number>> extends infer LastDigit extends number ? DecrementMap[LastDigit] extends infer Decremented extends number ? Number extends `${infer Rest}${LastDigit}` ? `${Decremented}` extends keyof NegativeCarryMap ? _Decrement<Rest, `${NegativeCarryMap[`${Decremented}`]}${Result}`> : `${Rest}${Decremented}${Result}` extends infer FinalResult extends string ? ParseNumber< FinalResult extends `0${infer FinalResultWithoutLeadingZero extends string}` ? FinalResultWithoutLeadingZero extends "" ? FinalResult : FinalResultWithoutLeadingZero : FinalResult > : never : never : never : never; type _DecrementNegativeOrZero<T extends number> = _Increment< Stringify<T> > extends infer PositiveDecrementResult extends number ? PositiveDecrementResult extends 0 ? PositiveDecrementResult : Negate<PositiveDecrementResult> : never; /** * Accepts an integer and returns the decremented value of it. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 5 * type Case1 = Decrement<6> * // -7 * type Case2 = Decrement<-6> * ``` */ type Decrement<T extends number> = IsNegative<T> extends true ? _DecrementNegativeOrZero<Abs<T>> : T extends 0 ? _DecrementNegativeOrZero<T> : _Decrement<Stringify<T>>; type IncrementMap = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; type LastDigitMap = { 10: 0; }; type _Increment< Number extends string, Result extends string = "" > = IsEmptyString<Number> extends true ? ParseNumber<`1${Result}`> : LastCharacter<Number> extends `${infer LastDigit extends number}` ? IncrementMap[LastDigit] extends infer Incremented extends number ? Number extends `${infer Rest}${LastDigit}` ? Incremented extends keyof LastDigitMap ? _Increment<Rest, `${LastDigitMap[Incremented]}${Result}`> : ParseNumber<`${Rest}${Incremented}${Result}`> : never : never : never; /** * Accepts an integer and returns the incremented value of it. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 2 * type Case1 = Increment<1> * // -9 * type Case1 = Increment<-10> * ``` */ type Increment<T extends number> = IsNegative<T> extends true ? _Decrement< Stringify<Abs<T>> > extends infer NegativeIncrementResult extends number ? NegativeIncrementResult extends 0 ? NegativeIncrementResult : Negate<NegativeIncrementResult> : never : _Increment<Stringify<T>>; /** * Returns a tuple of whole and fraction part of the passed float number * @example * ```ts * [12, 25] * type Case1 = GetFloatNumberParts<12.25> * [12, 25] * type Case2 = GetFloatNumberParts<-12.25> * ``` */ type GetFloatNumberParts<T extends number> = IsFloat<T> extends true ? `${Abs<T>}` extends `${infer Whole extends number}.${infer Fraction extends number}` ? [Whole, Fraction] : never : never; /** * Type version of `Math.ceil()`. Returns ceiled value of the passed number * @example * ```ts * // 2 * type Case1 = Ceil<1.2> * // -1 * type Case2 = Ceil<-1.2> * ``` */ type Ceil<T extends number> = IsFloat<T> extends true ? GetFloatNumberParts<T> extends [infer Whole extends number, unknown] ? IsNegative<T> extends true ? Negate<Whole> : Increment<Whole> : never : T; /** * Returns a boolean whether the passed two arguments are equal * @example * ```ts * // true * type Case1 = IsEqual<string, string> * // false * type Case2 = IsEqual<1, 4> * ``` */ type IsEqual<T, U> = (<F>() => F extends T ? 1 : 2) extends < F >() => F extends U ? 1 : 2 ? true : false; /** * Returns a boolean whether the passed two arguments are not equal * @example * ```ts * // true * type Case1 = IsNotEqual<1, 4> * // false * type Case2 = IsNotEqual<string, string> * ``` */ type IsNotEqual<T, U> = Not<IsEqual<T, U>>; /** * Accepts two types and returns the third argument if the first two are equal (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * @example * ```ts * // 'valid' * type Case1 = IfEqual<string, string, 'valid'> * // 'invalid' * type Case1 = IfEqual<1, 4, 'valid', 'invalid'> * ``` */ type IfEqual<T, U, IfTrue = true, IfFalse = false> = If< IsEqual<T, U>, IfTrue, IfFalse >; /** * Accepts two types and returns the third argument if the first two are not equal (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * @example * ```ts * // 'valid' * type Case1 = IfNotEqual<1, 4, 'valid'> * // 'invalid' * type Case1 = IfNotEqual<string, string, 'valid', 'invalid'> * ``` */ type IfNotEqual<T, U, IfTrue = true, IfFalse = false> = If< IsNotEqual<T, U>, IfTrue, IfFalse >; /** * Accepts a string and removes leading characters specified in the second argument * @example * ```ts * // 'bc' * type Case1 = RemoveLeading<'aaabc', 'a'> * // 'abc' * type Case2 = RemoveLeading<'abc', 'd'> * // '' * type Case3 = RemoveLeading<'aaa', 'a'> * // 'a' * type Case3 = RemoveLeading<'aaa', 'aa'> * ``` */ type RemoveLeading< T extends string, Characters extends string > = T extends `${Characters}${infer Rest extends string}` ? IsEmptyString<Rest> extends true ? Rest : RemoveLeading<Rest, Characters> : T; type SubDecrementMap = { "-9": -10; "-8": -9; "-7": -8; "-6": -7; "-5": -6; "-4": -5; "-3": -4; "-2": -3; "-1": -2; "0": -1; "1": 0; "2": 1; "3": 2; "4": 3; "5": 4; "6": 5; "7": 6; "8": 7; "9": 8; }; type SubNegativeCarryMap = { "-10": 0; "-9": 1; "-8": 2; "-7": 3; "-6": 4; "-5": 5; "-4": 6; "-3": 7; "-2": 8; "-1": 9; }; type SubMap = { 0: [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]; 1: [1, 0, -1, -2, -3, -4, -5, -6, -7, -8]; 2: [2, 1, 0, -1, -2, -3, -4, -5, -6, -7]; 3: [3, 2, 1, 0, -1, -2, -3, -4, -5, -6]; 4: [4, 3, 2, 1, 0, -1, -2, -3, -4, -5]; 5: [5, 4, 3, 2, 1, 0, -1, -2, -3, -4]; 6: [6, 5, 4, 3, 2, 1, 0, -1, -2, -3]; 7: [7, 6, 5, 4, 3, 2, 1, 0, -1, -2]; 8: [8, 7, 6, 5, 4, 3, 2, 1, 0, -1]; 9: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]; }; type _RemoveLeadingZeros<T extends string> = ParseNumber< RemoveLeading<T, "0"> extends infer WithoutLeadingZeros extends string ? IfEmptyString<WithoutLeadingZeros, "0", WithoutLeadingZeros> : never >; type _Sub< Num1 extends string, Num2 extends string, NegativeCarry extends 0 | 1 = 0, Result extends string = "" > = IsEmptyString<Num2> extends true ? NegativeCarry extends 0 ? `${Num1}${Result}` : `${Decrement<ParseNumber<Num1>>}${Result}` : LastCharacter<Num1> extends `${infer Num1LastDigit extends keyof SubMap & number}` ? LastCharacter<Num2> extends `${infer Num2LastDigit extends keyof SubMap[Num1LastDigit] & number}` ? `${SubMap[Num1LastDigit][Num2LastDigit]}` extends infer DigitsSub extends keyof SubDecrementMap ? ( NegativeCarry extends 1 ? Stringify<SubDecrementMap[DigitsSub]> : DigitsSub ) extends infer DigitsSubWithCarry extends string ? Num1 extends `${infer Num1Rest}${Num1LastDigit}` ? Num2 extends `${infer Num2Rest}${Num2LastDigit}` ? DigitsSubWithCarry extends keyof SubNegativeCarryMap ? _Sub< Num1Rest, Num2Rest, 1, `${SubNegativeCarryMap[DigitsSubWithCarry]}${Result}` > : _Sub<Num1Rest, Num2Rest, 0, `${DigitsSubWithCarry}${Result}`> : never : never : never : never : never : never; /** * Accepts two integers and returns their subtraction. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 8 * type Case1 = Sub<10, 2> * // -8 * type Case2 = Sub<2, 10> * // 12 * type Case3 = Sub<2, -10> * // -12 * type Case4 = Sub<-2, 10> * ``` */ type Sub< Num1 extends number, Num2 extends number > = IsNegativeInteger<Num1> extends true ? IsNegativeInteger<Num2> extends true ? IsLowerThan<Num1, Num2> extends true ? Negate< _RemoveLeadingZeros<_Sub<Stringify<Abs<Num1>>, Stringify<Abs<Num2>>>> > : _RemoveLeadingZeros<_Sub<Stringify<Abs<Num2>>, Stringify<Abs<Num1>>>> : Sum<Abs<Num1>, Num2> extends infer Result extends number ? Negate<Result> : never : IsNegativeInteger<Num2> extends true ? Sum<Num1, Abs<Num2>> : IsLowerThan<Num1, Num2> extends true ? Negate<_RemoveLeadingZeros<_Sub<Stringify<Num2>, Stringify<Num1>>>> : _RemoveLeadingZeros<_Sub<Stringify<Num1>, Stringify<Num2>>>; type SumIncrementMap = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ]; type SumLastDigitMap = { 10: 0; 11: 1; 12: 2; 13: 3; 14: 4; 15: 5; 16: 6; 17: 7; 18: 8; 19: 9; }; type SumMap = { 0: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; 2: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 3: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; 4: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]; 5: [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]; 6: [6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 7: [7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; 8: [8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; 9: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18]; }; type _Sum< Num1 extends string, Num2 extends string, Carry extends 0 | 1 = 0, Result extends string = "" > = IsEmptyString<Num1> extends true ? Carry extends 0 ? ParseNumber<`${Num2}${Result}`> : _Increment<Num2, Result> : IsEmptyString<Num2> extends true ? Carry extends 0 ? ParseNumber<`${Num1}${Result}`> : _Increment<Num1, Result> : LastCharacter<Num1> extends `${infer Num1LastDigit extends keyof SumMap & number}` ? LastCharacter<Num2> extends `${infer Num2LastDigit extends keyof SumMap[Num1LastDigit] & number}` ? SumMap[Num1LastDigit][Num2LastDigit] extends infer DigitsSum extends number ? ( Carry extends 1 ? SumIncrementMap[DigitsSum] : DigitsSum ) extends infer DigitsSumWithCarry extends number ? Num1 extends `${infer Num1Rest}${Num1LastDigit}` ? Num2 extends `${infer Num2Rest}${Num2LastDigit}` ? DigitsSumWithCarry extends keyof SumLastDigitMap ? _Sum< Num1Rest, Num2Rest, 1, `${SumLastDigitMap[DigitsSumWithCarry]}${Result}` > : _Sum<Num1Rest, Num2Rest, 0, `${DigitsSumWithCarry}${Result}`> : never : never : never : never : never : never; /** * Accepts two integers and returns their sum. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 13 * type Case1 = Sum<4, 9> * // 5 * type Case2 = Sum<-4, 9> * // -5 * type Case3 = Sum<4, -9> * // -13 * type Case4 = Sum<-4, -9> * ``` */ type Sum< Num1 extends number, Num2 extends number > = IsNegativeInteger<Num1> extends true ? IsNegativeInteger<Num2> extends true ? Negate<_Sum<Stringify<Abs<Num1>>, Stringify<Abs<Num2>>>> : Sub<Num2, Abs<Num1>> : IsNegativeInteger<Num2> extends true ? Sub<Num1, Abs<Num2>> : _Sum<Stringify<Num1>, Stringify<Num2>>; type _SumArr< T extends readonly number[], CurrentSum extends number = 0 > = IsEmptyArray<T> extends true ? CurrentSum : Pop<T, { includeRemoved: true }> extends infer PopResult ? IsNever<PopResult> extends true ? CurrentSum : PopResult extends [infer Rest extends number[], infer Num1 extends number] ? _SumArr<Rest, Sum<CurrentSum, Num1>> : never : CurrentSum; /** * Accepts an array of integers and returns the sum of its elements. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 10 * type Case1 = SumArr<[1, 2, 3, 4]> * // 2 * type Case2 = SumArr<[1, 2, 3, -4]> * ``` */ type SumArr<T extends readonly number[]> = IsTuple<T> extends true ? _SumArr<T> : never; type _StringLength< S extends string, Parts extends [string[], string[], string[], string[]] = [[], [], [], []] > = S extends "" ? Sum< Sum<Parts[0]["length"], Parts[1]["length"]>, Sum<Parts[2]["length"], Parts[3]["length"]> > : S extends `${infer C1 extends string}${infer Rest1 extends string}` ? Rest1 extends `${infer C2 extends string}${infer Rest2 extends string}` ? Rest2 extends `${infer C3 extends string}${infer Rest3 extends string}` ? Rest3 extends `${infer C4 extends string}${infer Rest4 extends string}` ? _StringLength< Rest4, [ [...Parts[0], C1], [...Parts[1], C2], [...Parts[2], C3], [...Parts[3], C4] ] > : _StringLength< Rest3, [[...Parts[0], C1], [...Parts[1], C2], [...Parts[2], C3], Parts[3]] > : _StringLength< Rest2, [[...Parts[0], C1], [...Parts[1], C2], Parts[2], Parts[3]] > : _StringLength<Rest1, [[...Parts[0], C1], Parts[1], Parts[2], Parts[3]]> : _StringLength<S, Parts>; /** * Returns the length of the passed string. Range of string length `[0, 3968]` * @example * ```ts * // 0 * type Case1 = StringLength<''> * // 3 * type Case2 = StringLength<'xxx'> * ``` */ type StringLength<S extends string> = _StringLength<S>; /** * Accepts two strings, returns the third argument (defaults to `never`) if the first string is shorter, otherwise returns the fourth argument (defaults to `never`) if the second argument is shorter, if strings have the same length returns the fifth argument (defaults to `never`) * @example * ```ts * // 'first shorter' * type Case1 = CompareStringLength<'a', 'ab', 'first shorter'> * // 'first longer' * type Case2 = CompareStringLength<'abc', 'ab', 'first shorter', 'first longer'> * // 'equal' * type Case3 = CompareStringLength<'ab', 'ab', 'first shorter', 'first longer', 'equal'> * ``` */ type CompareStringLength< Str1 extends string, Str2 extends string, IfStr1Shorter = never, IfStr2Shorter = never, IfEqual = never > = IsEmptyString<Str1> extends true ? IsEmptyString<Str2> extends true ? IfEqual : IfStr1Shorter : IsEmptyString<Str2> extends true ? IfStr2Shorter : Str1 extends `${string}${infer Str1Rest extends string}` ? Str2 extends `${string}${infer Str2Rest extends string}` ? CompareStringLength< Str1Rest, Str2Rest, IfStr1Shorter, IfStr2Shorter, IfEqual > : never : never; /** * Accepts two strings, returns a boolean whether the first string is shorter * @example * ```ts * // true * type Case1 = IsShorterString<'a', 'ab'> * // false * type Case2 = IsShorterString<'abc', 'ab'> * ``` */ type IsShorterString< Str1 extends string, Str2 extends string > = CompareStringLength<Str1, Str2, true, false, false>; /** * Accepts two strings, returns a boolean whether the first string is longer * @example * ```ts * // true * type Case1 = IsLongerString<'ab', 'a'> * // false * type Case2 = IsLongerString<'a', 'ab'> * ``` */ type IsLongerString< Str1 extends string, Str2 extends string > = CompareStringLength<Str1, Str2, false, true, false>; /** * Accepts two strings, returns a boolean whether strings have the same length * @example * ```ts * // true * type Case1 = IsSameLengthString<'ab', 'ab'> * // false * type Case2 = IsSameLengthString<'ab', 'abc'> * ``` */ type IsSameLengthString< Str1 extends string, Str2 extends string > = CompareStringLength<Str1, Str2, false, false, true>; /** * Returns number of digits of the passed number * @example * ```ts * // 3 * type Case1 = NumberLength<100> * // 15 * type Case2 = NumberLength<100000000000000> * ``` */ type NumberLength<T extends number> = StringLength<Stringify<T>>; /** * Accepts two numbers, returns the third argument (defaults to `never`) if the first number is shorter, otherwise returns the fourth argument (defaults to `never`) if the second argument is shorter, if numbers have the same length returns the fifth argument (defaults to `never`) * @example * ```ts * // 'first shorter' * type Case1 = CompareNumberLength<1, 12, 'first shorter'> * // 'first longer' * type Case2 = CompareNumberLength<123, 12, 'first shorter', 'first longer'> * // 'equal' * type Case3 = CompareNumberLength<12, 12, 'first shorter', 'first longer', 'equal'> * ``` */ type CompareNumberLength< Num1 extends number, Num2 extends number, IfNum1Shorter = never, IfNum2Shorter = never, IfEqual = never > = CompareStringLength< Stringify<Num1>, Stringify<Num2>, IfNum1Shorter, IfNum2Shorter, IfEqual >; /** * Accepts two numbers, returns a boolean whether the first number is shorter * @example * ```ts * // true * type Case1 = IsShorterNumber<1, 10> * // false * type Case2 = IsShorterNumber<100, 10> * ``` */ type IsShorterNumber< Num1 extends number, Num2 extends number > = CompareNumberLength<Num1, Num2, true, false, false>; /** * Accepts two numbers, returns a boolean whether the first number is longer * @example * ```ts * // true * type Case1 = IsLongerNumber<10, 1> * // false * type Case2 = IsLongerNumber<10, 100> * ``` */ type IsLongerNumber< Num1 extends number, Num2 extends number > = CompareNumberLength<Num1, Num2, false, true, false>; /** * Accepts two numbers, returns a boolean whether numbers have the same length * @example * ```ts * // true * type Case1 = IsSameLengthNumber<10, 10> * // false * type Case2 = IsSameLengthNumber<10, 100> * ``` */ type IsSameLengthNumber< Num1 extends number, Num2 extends number > = CompareNumberLength<Num1, Num2, false, false, true>; type LowerThanMap = { "0": ["1", "2", "3", "4", "5", "6", "7", "8", "9"]; "1": ["2", "3", "4", "5", "6", "7", "8", "9"]; "2": ["3", "4", "5", "6", "7", "8", "9"]; "3": ["4", "5", "6", "7", "8", "9"]; "4": ["5", "6", "7", "8", "9"]; "5": ["6", "7", "8", "9"]; "6": ["7", "8", "9"]; "7": ["8", "9"]; "8": ["9"]; "9": []; }; type _IsLowerThan< Num1 extends string, Num2 extends string > = Num1 extends `${infer Num1Character extends keyof LowerThanMap}${infer Num1Rest extends string}` ? Num2 extends `${infer Num2Character extends string}${infer Num2Rest extends string}` ? IsEqual<Num1Character, Num2Character> extends true ? _IsLowerThan<Num1Rest, Num2Rest> : Num2Character extends LowerThanMap[Num1Character][number] ? true : false : true : false; /** * Returns a boolean whether the first passed integer is lower than the second integer. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // true * type Case1 = IsLowerThan<1, 10> * // false * type Case2 = IsLowerThan<1, -10> * ``` */ type IsLowerThan<Num1 extends number, Num2 extends number> = IsEqual< Num1, Num2 > extends true ? false : IsNegative<Num1> extends true ? IsNegative<Num2> extends false ? true : CompareNumberLength< Num1, Num2, false, true, Not<_IsLowerThan<Stringify<Abs<Num1>>, Stringify<Abs<Num2>>>> > : IsNegative<Num2> extends true ? false : CompareNumberLength< Num1, Num2, true, false, _IsLowerThan<Stringify<Abs<Num1>>, Stringify<Abs<Num2>>> >; /** * Returns a boolean whether the first passed integer is lower than the second integer. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // 'valid' * type Case1 = IfLowerThan<1, 10, 'valid'> * // 'invalid' * type Case2 = IfLowerThan<1, -10, 'valid', 'invalid'> * ``` */ type IfLowerThan< Num1 extends number, Num2 extends number, IfTrue = true, IfFalse = false > = If<IsLowerThan<Num1, Num2>, IfTrue, IfFalse>; /** * Returns a boolean whether the first passed integer is lower or equal to the second integer. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // true * type Case1 = IsLowerOrEqual<1, 10> * // false * type Case2 = IsLowerOrEqual<1, -10> * ``` */ type IsLowerOrEqual<Num1 extends number, Num2 extends number> = IsEqual< Num1, Num2 > extends true ? true : IsLowerThan<Num1, Num2>; /** * Returns a boolean whether the first passed integer is greater than the second integer. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // true * type Case1 = IsGreaterThan<10, 1> * // false * type Case2 = IsGreaterThan<-10, 1> * ``` */ type IsGreaterThan< Num1 extends number, Num2 extends number > = IsLowerThan<Num2, Num1>; /** * Returns the third argument if the first argument (integer) is greater than the second argument (integer) (defaults to `true`), otherwise returns the fourth argument (defaults to `false`). Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * 'valid' * type Case1 = IfGreaterThan<10, 1, 'valid'> * // 'invalid' * type Case2 = IfGreaterThan<-10, 1, 'valid', 'invalid'> * ``` */ type IfGreaterThan< Num1 extends number, Num2 extends number, IfTrue = true, IfFalse = false > = IfLowerThan<Num2, Num1, IfTrue, IfFalse>; /** * Returns a boolean whether the first passed integer is greater than the second integer or equal. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // true * type Case1 = IsGreaterThan<10, 1> * // false * type Case2 = IsGreaterThan<-10, 1> * // true * type Case3 = IsGreaterThan<10, 10> * ``` */ type IsGreaterOrEqual< Num1 extends number, Num2 extends number > = IsEqual<Num1, Num2> extends true ? true : IsGreaterThan<Num1, Num2>; /** * Returns the third argument if the first argument (integer) is greater than the second argument (integer) or equal (defaults to `true`), otherwise returns the fourth argument (defaults to `false`). Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * 'valid' * type Case1 = IfGreaterThan<10, 1, 'valid'> * // 'invalid' * type Case2 = IfGreaterThan<-10, 1, 'valid', 'invalid'> * ``` */ type IfGreaterOrEqual< Num1 extends number, Num2 extends number, IfTrue = true, IfFalse = false > = If< IsEqual<Num1, Num2> extends true ? true : IsGreaterThan<Num1, Num2>, IfTrue, IfFalse >; type IsBetweenOptions = { minIncluded?: boolean; maxIncluded?: boolean; }; /** * Returns a boolean whether the first integer argument is between the second and the third integer argument * By default borders of the interval are included, which can be modified by the second argument. * `minIncluded`, `maxIncluded` options show whether to include the lower and the higher borders respectively. Range: `[Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]` * @example * ```ts * // true * type Case1 = IsBetween<1, 1, 10> * // false * type Case2 = IsBetween<1, 1, 10, {minIncluded: false}> * // false * type Case3 = IsBetween<10, 1, 10, {maxIncluded: false}> * ``` */ type IsBetween< Num extends number, Min extends number, Max extends number, Options extends IsBetweenOptions = { minIncluded: true; maxIncluded: true; } > = IsEqual<Num, Min> extends true ? Options["minIncluded"] : IsEqual<Num, Max> extends true ? Options["maxIncluded"] : And<IsGreaterThan<Num, Min>, IsLowerThan<Num, Max>>; /** * Type version of `String.prototype.split()`. Splits the first string argument by the second string argument * @example * ```ts * // ['a', 'b', 'c'] * type Case1 = Split<'abc', ''> * // ['a', 'b', 'c'] * type Case2 = Split<'a,b,c', ','> * ``` */ type Split< Str extends string, Del extends string | number > = string extends Str ? string[] : "" extends Str ? [] : Str extends `${infer T}${Del}${infer U}` ? [T, ...Split<U, Del>] : [Str]; type _IsValidRGBParameter<T extends number> = IsInteger<T> extends true ? IsBetween<T, 0, 255> : false; type RGBOptions = { separator: string; }; type DefaultRGBOptions = { separator: ", "; }; /** * Returns the first string argument if it is a valid RGB color, otherwise returns `never`. * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // rgb(23, 242, 0) * type Case1 = RGB<'rgb(23, 242, 0)'> * // never * type Case2 = RGB<'rgb(324, 123, 3)'> * // rgb(23,242,0) * type Case3 = RGB<'rgb(23,242,0)', { separator: ',' }> * ``` */ type RGB< T extends string, Options extends RGBOptions = DefaultRGBOptions > = T extends `rgb(${infer R extends number}${Options["separator"]}${infer G extends number}${Options["separator"]}${infer B extends number})` ? AndArr< [ _IsValidRGBParameter<R>, _IsValidRGBParameter<G>, _IsValidRGBParameter<B> ] > extends true ? T : never : never; /** * Returns a boolean whether the first string argument is a valid RGB color. * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // true * type Case1 = IsRGB<'rgb(23, 242, 0)'> * // false * type Case2 = IsRGB<'rgb(324, 123, 3)'> * // true * type Case3 = IsRGB<'rgb(23,242,0)', { separator: ',' }> * ``` */ type IsRGB< T extends string, Options extends RGBOptions = DefaultRGBOptions > = Not<IsNever<RGB<T, Options>>>; /** * Returns the third argument if the first argument is valid RGB color (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // 'true' * type Case1 = IfRGB<'rgb(23, 242, 0)', 'true'> * // 'invalid' * type Case2 = IfRGB<'rgb(324, 123, 3)', 'valid', 'invalid'> * // true * type Case3 = IfRGB<'rgb(23,242,0)', { separator: ',' }> * ``` */ type IfRGB< T extends string, Options extends RGBOptions = DefaultRGBOptions, IfTrue = true, IfFalse = false > = If<IsRGB<T, Options>, IfTrue, IfFalse>; type _ValidHEXCharacters = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]; type _AllowedHEXLength = 3 | 4 | 6 | 8; /** * Returns the first string argument if it is a valid HEX color, otherwise returns `never` * @example * // '#000' * type Case1 = HEX<'#000'> * // never * type Case2 = HEX<'#g00'> * // '#0000' * type Case3 = HEX<'#0000'> * // never * type Case4 = HEX<'#00000'> * // '#000000' * type Case5 = HEX<'#000000'> * // '#00000000' * type Case6 = HEX<'#00000000'> */ type HEX<T extends string> = ( Uppercase<T> extends `#${infer HEXWithoutHashTag extends string}` ? StringLength<HEXWithoutHashTag> extends _AllowedHEXLength ? ExtendsArr<Split<HEXWithoutHashTag, "">, _ValidHEXCharacters[number]> : false : false ) extends true ? T : never; /** * Returns a boolean whether the first string argument is a valid HEX color. * @example * ```ts * // true * type Case1 = IsHEX<'#000'> * // false * type Case2 = IsHEX<'#g00'> * ``` */ type IsHEX<T extends string> = Not<IsNever<HEX<T>>>; /** * Returns the second argument if the first argument is valid HEX color (defaults to `true`), otherwise returns the third argument (defaults to `false`) * @example * ```ts * // true * type Case1 = IfHEX<'#000'> * // false * type Case2 = IfHEX<'#g00'> * // 'valid' * type Case3 = IfHEX<'#0000', 'valid'> * // 'invalid' * type Case4 = IfHEX<'#00000', 'valid', 'invalid'> * ``` */ type IfHEX<T extends string, IfTrue = true, IfFalse = false> = If< IsHEX<T>, IfTrue, IfFalse >; type HSLOptions = { separator: string; }; type DefaultHSLOptions = { separator: ", "; }; /** * Returns the first string argument if it is a valid HSL color, otherwise returns `never`. * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // hsl(100, 34%, 56%) * type Case1 = HSL<'hsl(100, 34%, 56%)'> * // never * type Case2 = HSL<'hsl(100, 200%, 3)'> * // hsl(100,34%,56%) * type Case3 = HSL<'hsl(100,34%,56%)', { separator: ',' }> * ``` */ type HSL< T extends string, Options extends HSLOptions = DefaultHSLOptions > = ( T extends `hsl(${infer H extends number}${Options["separator"]}${infer S extends number}%${Options["separator"]}${infer L extends number}%)` ? AndArr<[IsInteger<H>, IsInteger<S>, IsInteger<L>]> extends true ? AndArr<[IsBetween<S, 0, 100>, IsBetween<L, 0, 100>]> : false : false ) extends true ? T : never; /** * Returns a boolean whether the first string argument is a valid HSL color. * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // true * type Case1 = IsHSL<'hsl(100, 34%, 56%)'> * // false * type Case2 = IsHSL<'hsl(101, 200%, 3)'> * // true * type Case3 = IsHSL<'hsl(100,34%,56%)', { separator: ',' }> * ``` */ type IsHSL< T extends string, Options extends HSLOptions = DefaultHSLOptions > = Not<IsNever<HSL<T, Options>>>; /** * Returns the third argument if the first argument is valid HSL color (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * The second argument is an object type with `separator: string` property, which shows the separator between color parameters (defaults to `', '`) * @example * ```ts * // 'true' * type Case1 = IfHSL<'hsl(100, 34%, 56%)', 'true'> * // 'invalid' * type Case2 = IfHSL<'hsl(101, 200%, 3)', 'valid', 'invalid'> * // true * type Case3 = IfHSL<'hsl(100,34%,56%)', { separator: ',' }> * ``` */ type IfHSL< T extends string, Options extends HSLOptions = DefaultHSLOptions, IfTrue = true, IfFalse = false > = If<IsHSL<T, Options>, IfTrue, IfFalse>; type ColorOptions = { rgbOptions?: RGBOptions; hslOptions?: HSLOptions; }; type DefaultColorOptions = { rgbOptions: DefaultRGBOptions; hslOptions: DefaultHSLOptions; }; /** * Returns the first string argument if it is a valid RGB or HEX or HSL color, otherwise returns `never`. * The second argument is an object type with `rgbOptions: RGBOptions` and `hslOptions: hslOptions` properties, which can accept the separator between color parameters (defaults to `', '`) * @example * ```ts * // rgb(23, 242, 0) * type Case1 = Color<'rgb(23, 242, 0)'> * // never * type Case2 = Color<'rgb(324, 123, 3)'> * // '#000000' * type Case3 = HEX<'#000000'> * // 'hsl(100,34%,56%)' * type Case4 = Color<'hsl(100,34%,56%)', { hslOptions: { separator: ',' } }> * ``` */ type Color< T extends string, Options extends ColorOptions = DefaultColorOptions > = RGB<T, Options["rgbOptions"]> | HEX<T> | HSL<T, Options["hslOptions"]>; /** * Returns a boolean whether the first string argument is a valid RGB or HEX or HSL color. * The second argument is an object type with `rgbOptions: RGBOptions` and `hslOptions: hslOptions` properties, which can accept the separator between color parameters (defaults to `', '`) * @example * ```ts * // true * type Case1 = Color<'rgb(23, 242, 0)'> * // false * type Case2 = Color<'rgb(324, 123, 3)'> * // true * type Case3 = HEX<'#000000'> * // true * type Case4 = Color<'hsl(100,34%,56%)', { hslOptions: { separator: ',' } }> * ``` */ type IsColor< T extends string, Options extends ColorOptions = DefaultColorOptions > = Not<IsNever<Color<T, Options>>>; /** * Returns the third argument if the first argument is valid RGB or HEX or HSL color (defaults to `true`), otherwise returns the fourth argument (defaults to `false`) * The second argument is an object type with `rgbOptions: RGBOptions` and `hslOptions: hslOptions` properties, which can accept the separator between color parameters (defaults to `', '`) * @example * ```ts * // 'valid' * type Case1 = Color<'rgb(23, 242, 0)', 'valid'> * // 'invalid' * type Case2 = Color<'rgb(324, 123, 3)', 'valid', 'invalid'> * // true * type Case3 = Color<'#000000'> * ``` */ type IfColor< T extends string, Options extends ColorOptions = DefaultColorOptions, IfTrue = true, IfFalse = false > = If<IsColor<T, Options>, IfTrue, IfFalse>; /** * Type version of `Array.prototype.concat()`. Concatenates two arrays into one. * @example * ```ts * // [number, number, string, string] * type Result = Concat<[number, number], [string, string]> * ``` */ type Concat<T extends readonly unknown[], U> = [ ...T, ...(U extends readonly unknown[] ? U : [U]) ]; /** * Accepts an integer argument and returns a tuple of its digits * @example * ```ts * // [1] * type Case1 = DigitsTuple<1> * // [1, 2, 3] * type Case2 = DigitsTuple<123> * // [1, 2, 3] * type Case3 = DigitsTuple<-123> * ``` */ type DigitsTuple<T extends number> = number extends T ? number[] : Split<Stringify<Abs<T>>, ""> extends infer Result ? { [K in keyof Result]: Result[K] extends string ? ParseNumber<Result[K]> : Result[K]; } : never; /** * Pushes the second argument to the first array argument * @example * ```ts * // [1, 2, 3, 4, 5] * type Case1 = Push<[1, 2, 3, 4], 5> * ``` */ type Push<T extends readonly unknown[], U extends unknown> = [...T, U]; type _Repeat< T extends string, Count extends number, Result extends string = "", Iteration extends unknown[] = [] > = Iteration["length"] extends Count ? Result : _Repeat<T, Count, `${T}${Result}`, Push<Iteration, unknown>>; /** * Repeats the first argument number of times specified in the second argument. Range `[0,999]` * @example * ```ts * // 'x' * type Case1 = Repeat<'x', 1> * // 'xxxxx' * type Case2 = Repeat<'x', 5> * ``` */ type Repeat<T extends string, Count extends number> = _Repeat<T, Count>; /** * Returns the first argument if it extends the second argument, returns the third argument otherwise * @example * ```ts * // 1 * type Case1 = ReturnItselfIfExtends<1, number, 2> * // 2 * type Case2 = ReturnItselfIfExtends<'1', number, 2> * ```