UNPKG

3.69 kBTypeScriptView Raw
1import type {UpperCaseCharacters, WordSeparators} from './internal';
2
3// Transforms a string that is fully uppercase into a fully lowercase version. Needed to add support for SCREAMING_SNAKE_CASE, see https://github.com/sindresorhus/type-fest/issues/385
4type UpperCaseToLowerCase<T extends string> = T extends Uppercase<T> ? Lowercase<T> : T;
5
6// This implementation does not support SCREAMING_SNAKE_CASE, it is used internally by `SplitIncludingDelimiters`.
7type SplitIncludingDelimiters_<Source extends string, Delimiter extends string> =
8 Source extends '' ? [] :
9 Source extends `${infer FirstPart}${Delimiter}${infer SecondPart}` ?
10 (
11 Source extends `${FirstPart}${infer UsedDelimiter}${SecondPart}`
12 ? UsedDelimiter extends Delimiter
13 ? Source extends `${infer FirstPart}${UsedDelimiter}${infer SecondPart}`
14 ? [...SplitIncludingDelimiters<FirstPart, Delimiter>, UsedDelimiter, ...SplitIncludingDelimiters<SecondPart, Delimiter>]
15 : never
16 : never
17 : never
18 ) :
19 [Source];
20
21/**
22Unlike a simpler split, this one includes the delimiter splitted on in the resulting array literal. This is to enable splitting on, for example, upper-case characters.
23
24@category Template literal
25*/
26export type SplitIncludingDelimiters<Source extends string, Delimiter extends string> = SplitIncludingDelimiters_<UpperCaseToLowerCase<Source>, Delimiter>;
27
28/**
29Format a specific part of the splitted string literal that `StringArrayToDelimiterCase<>` fuses together, ensuring desired casing.
30
31@see StringArrayToDelimiterCase
32*/
33type StringPartToDelimiterCase<StringPart extends string, Start extends boolean, UsedWordSeparators extends string, UsedUpperCaseCharacters extends string, Delimiter extends string> =
34 StringPart extends UsedWordSeparators ? Delimiter :
35 Start extends true ? Lowercase<StringPart> :
36 StringPart extends UsedUpperCaseCharacters ? `${Delimiter}${Lowercase<StringPart>}` :
37 StringPart;
38
39/**
40Takes the result of a splitted string literal and recursively concatenates it together into the desired casing.
41
42It receives `UsedWordSeparators` and `UsedUpperCaseCharacters` as input to ensure it's fully encapsulated.
43
44@see SplitIncludingDelimiters
45*/
46type StringArrayToDelimiterCase<Parts extends readonly any[], Start extends boolean, UsedWordSeparators extends string, UsedUpperCaseCharacters extends string, Delimiter extends string> =
47 Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
48 ? `${StringPartToDelimiterCase<FirstPart, Start, UsedWordSeparators, UsedUpperCaseCharacters, Delimiter>}${StringArrayToDelimiterCase<RemainingParts, false, UsedWordSeparators, UsedUpperCaseCharacters, Delimiter>}`
49 : Parts extends [string]
50 ? string
51 : '';
52
53/**
54Convert a string literal to a custom string delimiter casing.
55
56This can be useful when, for example, converting a camel-cased object property to an oddly cased one.
57
58@see KebabCase
59@see SnakeCase
60
61@example
62```
63import type {DelimiterCase} from 'type-fest';
64
65// Simple
66
67const someVariable: DelimiterCase<'fooBar', '#'> = 'foo#bar';
68
69// Advanced
70
71type OddlyCasedProperties<T> = {
72 [K in keyof T as DelimiterCase<K, '#'>]: T[K]
73};
74
75interface SomeOptions {
76 dryRun: boolean;
77 includeFile: string;
78 foo: number;
79}
80
81const rawCliOptions: OddlyCasedProperties<SomeOptions> = {
82 'dry#run': true,
83 'include#file': 'bar.js',
84 foo: 123
85};
86```
87
88@category Change case
89@category Template literal
90*/
91export type DelimiterCase<Value, Delimiter extends string> = string extends Value ? Value : Value extends string
92 ? StringArrayToDelimiterCase<
93 SplitIncludingDelimiters<Value, WordSeparators | UpperCaseCharacters>,
94 true,
95 WordSeparators,
96 UpperCaseCharacters,
97 Delimiter
98 >
99 : Value;