Skip to main content
Module

x/rimbu/typical/str.ts

Rimbu is a TypeScript library focused on immutable, performant, and type-safe collections and other tools.
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
import type { Num, U } from './index.ts';
/** * Returns the length of the given string S. * @example * ```ts * Length<'abc'> => 3 * ``` */export type Length<S extends string> = LengthHelper<S, 0>;
type LengthHelper<S extends string, Result extends number> = S extends '' ? Result : S extends `${string}${string}${string}${string}${string}${string}${string}${string}${string}${string}${infer Rest}` ? LengthHelper<Rest, Num.Add<Result, 10>> : S extends `${string}${string}${string}${string}${string}${infer Rest}` ? LengthHelper<Rest, Num.Add<Result, 5>> : S extends `${string}${infer Rest}` ? LengthHelper<Rest, Num.Add<Result, 1>> : Result;
/** * Convenience type to represent the concatenation of two string types. * @example * ```ts * Append<'abc', 'def'> => 'abcdef' * Append<'abc', 'd' | 'e'> => 'abcd' | 'abce' * ``` */export type Append<Start extends string, End extends string> = `${Start}${End}`;
/** * Convenience type to represent the concatenation of three string types. * @example * ```ts * AppendTwo<'abc', 'def', 'ghi'> => 'abcdefghi' * AppendTwo<'abc', 'd' | 'e', 'fgh'> => 'abcdfgh' | 'abcefgh' * ``` */export type AppendTwo< Start extends string, Middle extends string, End extends string> = `${Start}${Middle}${End}`;
/** * Returns false if the given string is empty, true otherwise. * @example * ```ts * IsNonEmptyString<''> => false * IsNonEmptyString<'abc'> => true * ``` */export type IsNonEmptyString<S extends string> = '' extends S ? false : true;
/** * Returns never if the given string is empty, otherwise the given string. * @example * ```ts * NonEmptyString<''> => never * NonEmptyString<'abc'> => 'abc' * ``` */export type NonEmptyString<S extends string> = '' extends S ? never : unknown;
/** * If the given string does not start with the given `Start` type, returns false. * Otherwise, returns a tuple containing the matched start and the rest. * @example * ```ts * StartsWith<'abcd', 'ab'> => ['ab', 'cd'] * StartsWith<'abcd', 'd'> => false * StartsWith<'abcd', 'ab' | 'bc'> => ['ab', 'cd'] * StartsWith<'abcd', 'ab' | 'a'> => ['ab', 'cd'] | ['a', 'bcd'] * ``` */export type StartsWith< S extends string, Start extends string & NonEmptyString<Start>> = S extends Append<Start, infer Rest> ? S extends Append<infer StartInstance, Rest> ? [StartInstance, Rest] : false : false;
/** * If the given string does not end with the given `End` type, returns false. * Otherwise, returns a tuple containing the start and the matched end. * @example * ```ts * EndsWith<'abcd', 'cd'> => ['ab', 'cd'] * EndsWith<'abcd', 'a'> => false * EndsWith<'abcd', 'cd' | 'de'> => ['ab', 'cd'] * EndsWith<'abcd', 'cd' | 'd'> => ['ab', 'cd'] | ['abc', 'd'] * ``` */export type EndsWith< S extends string, End extends string & NonEmptyString<End>> = S extends Append<infer Start, End> ? S extends Append<Start, infer EndInstance> ? [Start, EndInstance] : false : false;
/** * Returns false if the given string does not contain the given `Middle` type, * or a 3-tuple containing the start, the matched middle, and the rest. * @example * ```ts * SplitAt<'abcd', 'bc'> => ['a', 'bc', 'd'] * SplitAt<'abcd', 'ef'> => false * SplitAt<'abcd', 'b' | 'c'> => ['a', 'b', 'cd'] | ['ab', 'c', 'd'] * ``` */export type SplitAt< S extends string, Middle extends string & NonEmptyString<Middle>> = S extends AppendTwo<infer Start, Middle, infer End> ? S extends AppendTwo<Start, infer MiddleInstance, End> ? [Start, MiddleInstance, End] : false : ['', '', S];
/** * Returns a string containing all the elements that do not match the given Sub type. * @example * ```ts * FilterNot<'abcd', 'b'> => 'acd' * FilterNot<'abcd', 'b' | 'd'> => 'ac' * ``` */export type FilterNot< S extends string, Sub extends string & NonEmptyString<Sub>> = FilterNotHelper<S, Sub, ''>;
type FilterNotHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Result extends string> = S extends '' ? Result : S extends Append<Sub, infer Rest> ? FilterNotHelper<Rest, Sub, Result> : S extends Append<infer First, infer Rest> ? FilterNotHelper<Rest, Sub, Append<Result, First>> : '';
/** * Returns a string containing all the elements that match the given Sub type. * @example * ```ts * Filter<'abcd', 'b'> => 'b' * Filter<'abcd', 'b' | 'd'> => 'bd' * ``` */
export type Filter< S extends string, Sub extends string & NonEmptyString<Sub>> = FilterHelper<S, Sub, ''>;
type FilterHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Result extends string> = S extends '' ? Result : StartsWith<S, Sub> extends [infer SubInstance, infer Rest] ? FilterHelper<string & Rest, Sub, Append<Result, string & SubInstance>> : S extends Append<string, infer Rest> ? FilterHelper<Rest, Sub, Result> : '';
/** * Replaces, in the given string, all matches with Sub with the given Repl. * @example * ```ts * ReplaceAll<'abcba', 'b', '_'> => 'a_c_a' * ReplaceAll<'abcba', 'b' | 'c', '_'> => 'a___a' * ``` */export type ReplaceAll< S extends string, Sub extends string & NonEmptyString<Sub>, Repl extends string> = S extends '' ? '' : S extends Append<Sub, infer Rest> ? Append<Repl, ReplaceAll<Rest, Sub, Repl>> : S extends Append<infer Start, infer Rest> ? Append<Start, ReplaceAll<Rest, Sub, Repl>> : never;
/** * Replaces, in the given string, the first match with Sub with the given Repl. * Returns never if there was no match. * @example * ```ts * ReplaceFirst<'abcba', 'b', '_'> => 'a_cba' * ReplaceFirst<'abcba', 'b' | 'c', '_'> => 'a_cba' * ``` */export type ReplaceFirst< S extends string, Sub extends string & NonEmptyString<Sub>, Repl extends string> = S extends '' ? never : S extends Append<Sub, infer Rest> ? Append<Repl, Rest> : S extends Append<infer Start, infer Rest> ? Append<Start, ReplaceFirst<Rest, Sub, Repl>> : never;
/** * Replaces, in the given string, the last match with Sub with the given Repl. * Returns never if there was no match. * @example * ```ts * ReplaceLast<'abcba', 'b', '_'> => 'abc_a' * ReplaceLast<'abcba', 'b' | 'c', '_'> => 'abc_a' * ``` */export type ReplaceLast< S extends string, Sub extends string & NonEmptyString<Sub>, Repl extends string> = ReplaceLastHelper<S, Sub, Repl, false>;
type ReplaceLastHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Repl extends string, Replaced extends boolean> = S extends '' ? Replaced extends true ? '' : never : StartsWith<S, Sub> extends [infer SubInstance, infer Rest] ? ReplaceLastHelper<string & Rest, Sub, Repl, true> extends infer Result ? Append< Result extends Rest ? string & Repl : string & SubInstance, string & Result > : never : S extends Append<infer Start, infer Rest> ? Append<Start, ReplaceLastHelper<Rest, Sub, Repl, Replaced>> : never;
/** * Returns the amount of times the given `Sub` type is encountered in the given string. * @example * ```ts * Count<'abcba', 'c'> => 1 * Count<'abcba', 'a' | 'c'> => 3 * Count<'abcba', 'q'> => 0 * ``` */export type Count< S extends string, Sub extends string & NonEmptyString<Sub>> = CountHelper<S, Sub, 0>;
type CountHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Result extends number> = S extends '' ? Result : S extends Append<Sub, infer Rest> ? CountHelper<Rest, Sub, Num.Inc<Result>> : S extends Append<string, infer Rest> ? CountHelper<Rest, Sub, Result> : Result;
/** * Returns true if the given string contains the given Amount (default 1) of Sub types. * @example * ```ts * Contains<'abcba', 'b'> => true * Contains<'abcba', 'b', 2> => true * Contains<'abcba', 'b', 3> => false * Contains<'abcba', 'q'> => false * ``` */export type Contains< S extends string, Sub extends string & NonEmptyString<Sub>, Amount extends number = 1> = Amount extends 0 ? true : S extends Append<Sub, infer Rest> ? Contains<Rest, Sub, Num.Decr<Amount>> : S extends Append<string, infer Rest> ? Contains<Rest, Sub, Amount> : false;
/** * Returns true if the given string does not contain the given Amount (default 1) of Sub types. * @example * ```ts * NotContains<'abcba', 'b'> => false * NotContains<'abcba', 'b', 2> => false * NotContains<'abcba', 'b', 3> => true * NotContains<'abcba', 'q'> => true * ``` */export type NotContains< S extends string, Sub extends string & NonEmptyString<Sub>, Amount extends number = 1> = Contains<S, Sub, Amount> extends false ? true : false;
export type RepeatTimes< S extends string, Sub extends string & NonEmptyString<Sub>> = RepeatTimesHelper<S, Sub, [0, '']>;
type RepeatTimesHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Result extends [number, string]> = S extends '' ? [...Result, S] : StartsWith<S, Sub> extends [infer SubInstance, infer Rest] ? RepeatTimesHelper< string & Rest, Sub, [Num.Inc<Result[0]>, Append<Result[1], string & SubInstance>] > : [...Result, S];
/** * Returns a tuple containing the matched part and the rest of the given string if the string * start repeats the given Sub at least N times. * @example * ```ts * RepeatAtLeastTimes<'aabc', 'a', 0> => ['', 'aabc'] * RepeatAtLeastTimes<'aabc', 'a', 1> => ['a', 'abc'] * RepeatAtLeastTimes<'aabc', 'a', 3> => false * RepeatAtLeastTimes<'aabc', 'a' | 'b', 3> => ['aab', 'c'] * ``` */export type RepeatAtLeastTimes< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number> = RepeatAtLeastTimesHelper<S, Sub, N, ''>;
type RepeatAtLeastTimesHelper< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number, Processed extends string> = N extends 0 ? [Processed, S] : S extends '' ? false : StartsWith<S, Sub> extends [infer SubInstance, infer Rest] ? RepeatAtLeastTimesHelper< string & Rest, Sub, Num.Decr<N>, Append<Processed, string & SubInstance> > : false;
/** * Returns a tuple containing the matched part and the rest of the given string if the string * start repeats the given Sub at most N times. * @example * ```ts * RepeatAtMostTimes<'aabc', 'a', 0> => false * RepeatAtMostTimes<'aabc', 'a', 1> => false * RepeatAtMostTimes<'aabc', 'a', 3> => ['aa', 'bc'] * RepeatAtMostTimes<'aabc', 'a' | 'b', 3> => ['aab', 'c'] * ``` */export type RepeatAtMostTimes< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number> = RepeatAtMostTimesHelper<S, Sub, N, ''>;
type RepeatAtMostTimesHelper< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number, Processed extends string> = S extends Append<Sub, infer Rest> ? N extends 0 ? false : S extends Append<infer SubInstance, Rest> ? RepeatAtMostTimesHelper< Rest, Sub, Num.Decr<N>, Append<Processed, SubInstance> > : never : [Processed, S];
/** * Returns a tuple containing the matched part and the rest of the given string if the string * start repeats the given Sub exactly N times. * @example * ```ts * RepeatExactTimes<'aabc', 'a', 0> => false * RepeatExactTimes<'aabc', 'a', 1> => false * RepeatExactTimes<'aabc', 'a', 2> => ['aa', 'bc'] * RepeatExactTimes<'aabc', 'a', 3> => false * RepeatExactTimes<'aabc', 'a' | 'b', 3> => ['aab', 'c'] * ``` */export type RepeatExactTimes< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number> = RepeatExactTimesHelper<S, Sub, N, ''>;
type RepeatExactTimesHelper< S extends string, Sub extends string & NonEmptyString<Sub>, N extends number, Processed extends string> = S extends Append<Sub, infer Rest> ? N extends 0 ? false : S extends Append<infer SubInstance, Rest> ? RepeatExactTimesHelper< Rest, Sub, Num.Decr<N>, Append<Processed, SubInstance> > : never : N extends 0 ? [Processed, S] : false;
/** * Returns the first N characters of the given string, or false if the string does * not have enough characters. * @example * ```ts * TakeStrict<'abcd', 2> => 'ab' * TakeStrict<'abcd', 5> => false * ``` */export type TakeStrict<S extends string, N extends number> = TakeStrictHelper< S, N, ''>;
type TakeStrictHelper< S extends string, N extends number, Result extends string> = N extends 0 ? Result : S extends Append<infer First, infer Rest> ? TakeStrictHelper<Rest, Num.Decr<N>, Append<Result, First>> : false;
/** * Returns the first N characters of the given string, or the given * string if it does not have enough characters. * @example * ```ts * Take<'abcd', 2> => 'ab' * Take<'abcd', 5> => 'abcd' * ``` */export type Take<S extends string, N extends number> = TakeHelper<S, N, ''>;
type TakeHelper< S extends string, N extends number, Result extends string> = N extends 0 ? Result : S extends Append<infer First, infer Rest> ? TakeHelper<Rest, Num.Decr<N>, Append<Result, First>> : Result;
/** * Returns part of the string as long as its parts match Sub. * @example * ```ts * TakeWhile<'aabc', 'a'> => 'aa' * TakeWhile<'aabc', 'a' | 'b'> => 'aab' * TakeWhile<'aabc', 'q'> => '' * ``` */export type TakeWhile< S extends string, Sub extends string & NonEmptyString<Sub>> = TakeWhileHelper<S, Sub, ''>;
type TakeWhileHelper< S extends string, Sub extends string & NonEmptyString<Sub>, Result extends string> = StartsWith<S, Sub> extends [infer SubInstance, infer Rest] ? TakeWhileHelper<string & Rest, Sub, Append<Result, string & SubInstance>> : Result;
/** * Skips part of the string as long as its parts match Sub. * @example * ```ts * DropWhile<'aabc', 'a'> => 'bc' * DropWhile<'aabc', 'a' | 'b'> => 'c' * DropWhile<'aabc', 'q'> => 'aabc' * ``` */export type DropWhile<S extends string, Sub extends string> = S extends Append< Sub, infer Rest> ? DropWhile<Rest, Sub> : S;
/** * Returns the given string reversed. * @example * ```ts * Reverse<'abcd'> => 'dcba' * ``` */export type Reverse<S extends string> = ReverseHelper<S, ''>;
type ReverseHelper<S extends string, Result extends string> = S extends Append< infer First, infer Rest> ? ReverseHelper<Rest, Append<First, Result>> : Result;
/** * Returns the given string without the first N characters, or false if the * string has less characters. * @example * ```ts * DropStrict<'abcd', 2> => 'cd' * DropStrict<'abcd', 5> => false * ``` */export type DropStrict<S extends string, N extends number> = N extends 0 ? S : S extends Append<string, infer Rest> ? DropStrict<Rest, Num.Decr<N>> : false;
/** * Returns the given string without the first N characters, or an empty string if the * string has less characters. * @example * ```ts * Drop<'abcd', 2> => 'cd' * Drop<'abcd', 5> => '' * ``` */export type Drop<S extends string, N extends number> = N extends 0 ? S : S extends Append<string, infer Rest> ? Drop<Rest, Num.Decr<N>> : S;
/** * Returns the first character of the given string, or false if the string is empty. * @example * ```ts * First<'abc'> => 'a' * First<''> => false * ``` */export type First<S extends string> = S extends Append<infer First, string> ? First : false;
/** * Returns all but the first character of the given string, or false if the string is empty. * @example * ```ts * Tail<'abcd'> => 'bcd' * Tail<''> => false * ``` */export type Tail<S extends string> = S extends Append<string, infer Rest> ? Rest : false;
/** * Returns all but the last character of the given string, or false if the string is empty. * @example * ```ts * Init<'abcd'> => 'abc' * Init<''> => false * ``` */export type Init<S extends string> = InitHelper<S, ''>;
type InitHelper<S extends string, Result extends string> = S extends Append< infer First, infer Rest> ? U.Extends<Rest, '', Result, InitHelper<Rest, Append<Result, First>>> : false;
/** * Returns the last character of the given string, or false if the string if empty. * @example * ```ts * Last<'abcd'> => 'd' * Last<''> => false * ``` */export type Last<S extends string> = S extends Append<infer First, infer Rest> ? U.Extends<Rest, '', First, Last<Rest>> : false;
/** * Returns the character in the given string at the given Index, or false if the index * is out of bounds. * @example * ```ts * CharAt<'abcd', 1> => 'b' * CharAt<'abcd', 5> => false * ``` */export type CharAt<S extends string, Index extends number> = S extends Append< infer Start, infer Rest> ? U.Extends<Index, 0, Start, CharAt<Rest, Num.Decr<Index>>> : false;