import type {IsLowerCase, IsNumeric, IsUpperCase, WordSeparators} from './internal'; type SkipEmptyWord = Word extends '' ? [] : [Word]; type RemoveLastCharacter = Sentence extends `${infer LeftSide}${Character}` ? SkipEmptyWord : never; /** Split a string (almost) like Lodash's `_.words()` function. - Split on each word that begins with a capital letter. - Split on each {@link WordSeparators}. - Split on numeric sequence. @example ``` import type {Words} from 'type-fest'; type Words0 = Words<'helloWorld'>; //=> ['hello', 'World'] type Words1 = Words<'helloWORLD'>; //=> ['hello', 'WORLD'] type Words2 = Words<'hello-world'>; //=> ['hello', 'world'] type Words3 = Words<'--hello the_world'>; //=> ['hello', 'the', 'world'] type Words4 = Words<'lifeIs42'>; //=> ['life', 'Is', '42'] ``` @category Change case @category Template literal */ export type Words< Sentence extends string, LastCharacter extends string = '', CurrentWord extends string = '', > = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}` ? FirstCharacter extends WordSeparators // Skip word separator ? [...SkipEmptyWord, ...Words] : LastCharacter extends '' // Fist char of word ? Words // Case change: non-numeric to numeric, push word : [false, true] extends [IsNumeric, IsNumeric] ? [...SkipEmptyWord, ...Words] // Case change: numeric to non-numeric, push word : [true, false] extends [IsNumeric, IsNumeric] ? [...SkipEmptyWord, ...Words] // No case change: concat word : [true, true] extends [IsNumeric, IsNumeric] ? Words // Case change: lower to upper, push word : [true, true] extends [IsLowerCase, IsUpperCase] ? [...SkipEmptyWord, ...Words] // Case change: upper to lower, brings back the last character, push word : [true, true] extends [IsUpperCase, IsLowerCase] ? [...RemoveLastCharacter, ...Words] // No case change: concat word : Words : [...SkipEmptyWord];