Comparing version 0.5.1 to 1.0.0
/** | ||
* Removes all the elements matching the given condition from a tuple. | ||
* Gets the character at the given index. | ||
* T: The string to get the character from. | ||
* index: The index of the character. | ||
*/ | ||
type Drop<tuple, cond, output extends any[] = []> = tuple extends [ | ||
infer first, | ||
...infer rest | ||
] ? Drop<rest, cond, first extends cond ? output : [...output, first]> : output; | ||
type CharAt<T extends string, index extends number> = Split<T, ''>[index]; | ||
/** | ||
* Removes the given suffix from a sentence. | ||
* A strongly typed version of `String.prototype.charAt`. | ||
* @param str the string to get the character from. | ||
* @param index the index of the character. | ||
* @returns the character in both type level and runtime. | ||
* @example charAt('hello world', 6) // 'w' | ||
*/ | ||
type DropSuffix<sentence extends string, suffix extends string> = sentence extends `${infer rest}${suffix}` ? rest : sentence; | ||
declare function charAt<T extends string, I extends number>(str: T, index: I): CharAt<T, I>; | ||
/** | ||
* PascalCases all the words in a tuple of strings | ||
*/ | ||
type PascalCaseAll<T extends string[]> = T extends [infer First, ...infer Rest] ? [ | ||
Capitalize<Lowercase<Is<First, string>>>, | ||
...PascalCaseAll<Is<Rest, string[]>> | ||
] : T; | ||
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; | ||
type Separator = ' ' | '_' | '-' | '.' | '/'; | ||
/** | ||
* Assures the generic matches the given condition. | ||
*/ | ||
type Is<T, cond> = Extract<T, cond>; | ||
type UpperChars = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'; | ||
type LowerChars = Lowercase<UpperChars>; | ||
/** | ||
* Checks if the given character is an upper case letter. | ||
*/ | ||
type IsUpper<T extends string> = T extends UpperChars ? true : false; | ||
/** | ||
* Checks if the given character is a letter. | ||
*/ | ||
type IsLetter<T extends string> = IsUpper<T> extends true ? true : IsLower<T> extends true ? true : false; | ||
/** | ||
* Checks if the given character is a lower case letter. | ||
*/ | ||
type IsLower<T extends string> = T extends LowerChars ? true : false; | ||
/** | ||
* Checks if the given character is a number. | ||
*/ | ||
type IsDigit<T extends string> = T extends Digit ? true : false; | ||
/** | ||
* Checks if the given character is a separator. | ||
* E.g. space, underscore, dash, dot, slash. | ||
*/ | ||
type IsSeparator<T extends string> = T extends Separator ? true : false; | ||
/** | ||
* Checks if the given character is a special character. | ||
* E.g. not a letter, number, or separator. | ||
*/ | ||
type IsSpecial<T extends string> = IsLetter<T> extends true ? false : IsDigit<T> extends true ? false : IsSeparator<T> extends true ? false : true; | ||
/** | ||
* Splits a string into words. | ||
* sentence: The current string to split. | ||
* word: The current word. | ||
* prev: The previous character. | ||
*/ | ||
type Words<sentence extends string, word extends string = '', prev extends string = ''> = string extends sentence ? string[] : sentence extends `${infer curr}${infer rest}` ? IsSeparator<curr> extends true ? Drop<[word, ...Words<rest>], ''> : prev extends '' ? Drop<Words<rest, curr, curr>, ''> : [false, true] extends [IsDigit<prev>, IsDigit<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, false] extends [IsDigit<prev>, IsDigit<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [false, true] extends [IsSpecial<prev>, IsSpecial<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, false] extends [IsSpecial<prev>, IsSpecial<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, true] extends [IsDigit<prev>, IsDigit<curr>] ? Drop<Words<rest, `${word}${curr}`, curr>, ''> : [true, true] extends [IsLower<prev>, IsUpper<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, true] extends [IsUpper<prev>, IsLower<curr>] ? [ | ||
DropSuffix<word, prev>, | ||
...Words<rest, `${prev}${curr}`, curr> | ||
] : Drop<Words<rest, `${word}${curr}`, curr>, ''> : Drop<[word], ''>; | ||
/** | ||
* A strongly typed function to extract the words from a sentence. | ||
* @param sentence the sentence to extract the words from. | ||
* @returns an array of words in both type level and runtime. | ||
* @example words('helloWorld') // ['hello', 'World'] | ||
*/ | ||
declare function words<T extends string>(sentence: T): Words<T>; | ||
/** | ||
* Joins a tuple of strings with the given delimiter. | ||
* T: The current tuple of strings. | ||
* T: The tuple of strings to join. | ||
* delimiter: The delimiter. | ||
*/ | ||
type Join<T extends string[], delimiter extends string = ''> = string[] extends T ? string : T extends [infer first, ...infer rest] ? rest extends [] ? first : `${Is<first, string>}${delimiter}${Join<Is<rest, string[]>, delimiter>}` : ''; | ||
type Join<T extends readonly string[], delimiter extends string = ''> = string[] extends T ? string : T extends readonly [ | ||
infer first extends string, | ||
...infer rest extends string[] | ||
] ? rest extends [] ? first : `${first}${delimiter}${Join<rest, delimiter>}` : ''; | ||
/** | ||
@@ -100,6 +31,6 @@ * A strongly typed version of `Array.prototype.join`. | ||
*/ | ||
declare function join<T extends string[], D extends string = ''>(tuple: T, delimiter?: D): Join<T, D>; | ||
declare function join<const T extends readonly string[], D extends string = ''>(tuple: T, delimiter?: D): Join<T, D>; | ||
/** | ||
* Replaces the first occurrence of a string with another string. | ||
* sentence: The current sentence. | ||
* sentence: The sentence to replace. | ||
* lookup: The lookup string to be replaced. | ||
@@ -120,3 +51,3 @@ * replacement: The replacement string. | ||
* Replaces all the occurrences of a string with another string. | ||
* sentence: The current sentence. | ||
* sentence: The sentence to replace. | ||
* lookup: The lookup string to be replaced. | ||
@@ -140,3 +71,3 @@ * replacement: The replacement string. | ||
*/ | ||
type Split<T, delimiter extends string> = T extends `${infer first}${delimiter}${infer rest}` ? [first, ...Split<rest, delimiter>] : [T]; | ||
type Split<T, delimiter extends string = ''> = T extends `${infer first}${delimiter}${infer rest}` ? [first, ...Split<rest, delimiter>] : T extends '' ? [] : [T]; | ||
/** | ||
@@ -188,2 +119,88 @@ * A strongly typed version of `String.prototype.split`. | ||
/** | ||
* Removes all the elements matching the given condition from a tuple. | ||
*/ | ||
type Drop<tuple, cond, output extends any[] = []> = tuple extends [ | ||
infer first, | ||
...infer rest | ||
] ? Drop<rest, cond, first extends cond ? output : [...output, first]> : output; | ||
/** | ||
* Removes the given suffix from a sentence. | ||
*/ | ||
type DropSuffix<sentence extends string, suffix extends string> = sentence extends `${infer rest}${suffix}` ? rest : sentence; | ||
/** | ||
* PascalCases all the words in a tuple of strings | ||
*/ | ||
type PascalCaseAll<T extends string[]> = T extends [infer First, ...infer Rest] ? [ | ||
Capitalize<Lowercase<Is<First, string>>>, | ||
...PascalCaseAll<Is<Rest, string[]>> | ||
] : T; | ||
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'; | ||
type Separator = ' ' | '_' | '-' | '.' | '/'; | ||
/** | ||
* Assures the generic matches the given condition. | ||
*/ | ||
type Is<T, cond> = Extract<T, cond>; | ||
type UpperChars = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'; | ||
type LowerChars = Lowercase<UpperChars>; | ||
/** | ||
* Checks if the given character is an upper case letter. | ||
*/ | ||
type IsUpper<T extends string> = T extends UpperChars ? true : false; | ||
/** | ||
* Checks if the given character is a letter. | ||
*/ | ||
type IsLetter<T extends string> = IsUpper<T> extends true ? true : IsLower<T> extends true ? true : false; | ||
/** | ||
* Checks if the given character is a lower case letter. | ||
*/ | ||
type IsLower<T extends string> = T extends LowerChars ? true : false; | ||
/** | ||
* Checks if the given character is a number. | ||
*/ | ||
type IsDigit<T extends string> = T extends Digit ? true : false; | ||
/** | ||
* Checks if the given character is a separator. | ||
* E.g. space, underscore, dash, dot, slash. | ||
*/ | ||
type IsSeparator<T extends string> = T extends Separator ? true : false; | ||
/** | ||
* Checks if the given character is a special character. | ||
* E.g. not a letter, number, or separator. | ||
*/ | ||
type IsSpecial<T extends string> = IsLetter<T> extends true ? false : IsDigit<T> extends true ? false : IsSeparator<T> extends true ? false : true; | ||
/** | ||
* Splits a string into words. | ||
* sentence: The current string to split. | ||
* word: The current word. | ||
* prev: The previous character. | ||
*/ | ||
type Words<sentence extends string, word extends string = '', prev extends string = ''> = string extends sentence ? string[] : sentence extends `${infer curr}${infer rest}` ? IsSeparator<curr> extends true ? Drop<[word, ...Words<rest>], ''> : prev extends '' ? Drop<Words<rest, curr, curr>, ''> : [false, true] extends [IsDigit<prev>, IsDigit<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, false] extends [IsDigit<prev>, IsDigit<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [false, true] extends [IsSpecial<prev>, IsSpecial<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, false] extends [IsSpecial<prev>, IsSpecial<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, true] extends [IsDigit<prev>, IsDigit<curr>] ? Drop<Words<rest, `${word}${curr}`, curr>, ''> : [true, true] extends [IsLower<prev>, IsUpper<curr>] ? [ | ||
word, | ||
...Words<rest, curr, curr> | ||
] : [true, true] extends [IsUpper<prev>, IsLower<curr>] ? [ | ||
DropSuffix<word, prev>, | ||
...Words<rest, `${prev}${curr}`, curr> | ||
] : Drop<Words<rest, `${word}${curr}`, curr>, ''> : Drop<[word], ''>; | ||
/** | ||
* A strongly typed function to extract the words from a sentence. | ||
* @param sentence the sentence to extract the words from. | ||
* @returns an array of words in both type level and runtime. | ||
* @example words('helloWorld') // ['hello', 'World'] | ||
*/ | ||
declare function words<T extends string>(sentence: T): Words<T>; | ||
/** | ||
* This function is a strongly-typed counterpart of String.prototype.toLowerCase. | ||
@@ -299,3 +316,3 @@ * @param str the string to make lowercase. | ||
/** | ||
* Transforms the keys of an Record to camelCase. | ||
* Recursively transforms the keys of an Record to camelCase. | ||
* T: the type of the Record to transform. | ||
@@ -309,2 +326,9 @@ */ | ||
/** | ||
* Shallowly transforms the keys of an Record to camelCase. | ||
* T: the type of the Record to transform. | ||
*/ | ||
type CamelKeys<T> = T extends [] ? T : { | ||
[K in keyof T as CamelCase<Is<K, string>>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to camelCase. The transformation is done both at runtime and type level. | ||
@@ -317,3 +341,10 @@ * @param obj the object to transform. | ||
/** | ||
* Transforms the keys of an Record to PascalCase. | ||
* A strongly typed function that shallowly transforms the keys of an object to camelCase. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @returns the transformed object. | ||
* @example camelKeys({ 'foo-bar': { 'fizz-buzz': true } }) // { fooBar: { 'fizz-buz': true } } | ||
*/ | ||
declare function camelKeys<T>(obj: T): CamelKeys<T>; | ||
/** | ||
* Recursively transforms the keys of an Record to PascalCase. | ||
* T: the type of the Record to transform. | ||
@@ -327,2 +358,9 @@ */ | ||
/** | ||
* Shallowly transforms the keys of an Record to PascalCase. | ||
* T: the type of the Record to transform. | ||
*/ | ||
type PascalKeys<T> = T extends [] ? T : { | ||
[K in keyof T as PascalCase<Is<K, string>>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to pascal case. The transformation is done both at runtime and type level. | ||
@@ -335,3 +373,10 @@ * @param obj the object to transform. | ||
/** | ||
* Transforms the keys of an Record to kebab-case. | ||
* A strongly typed function that shallowly transforms the keys of an object to pascal case. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @returns the transformed object. | ||
* @example pascalKeys({ 'foo-bar': { 'fizz-buzz': true } }) // { FooBar: { 'fizz-buzz': true } } | ||
*/ | ||
declare function pascalKeys<T>(obj: T): PascalKeys<T>; | ||
/** | ||
* Recursively transforms the keys of an Record to kebab-case. | ||
* T: the type of the Record to transform. | ||
@@ -345,2 +390,9 @@ */ | ||
/** | ||
* Shallowly transforms the keys of an Record to kebab-case. | ||
* T: the type of the Record to transform. | ||
*/ | ||
type KebabKeys<T> = T extends [] ? T : { | ||
[K in keyof T as KebabCase<Is<K, string>>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to kebab-case. The transformation is done both at runtime and type level. | ||
@@ -353,3 +405,10 @@ * @param obj the object to transform. | ||
/** | ||
* Transforms the keys of an Record to snake_case. | ||
* A strongly typed function that shallowly transforms the keys of an object to kebab-case. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @returns the transformed object. | ||
* @example kebabKeys({ fooBar: { fizzBuzz: true } }) // { 'foo-bar': { fizzBuzz: true } } | ||
*/ | ||
declare function kebabKeys<T>(obj: T): KebabKeys<T>; | ||
/** | ||
* Recursively transforms the keys of an Record to snake_case. | ||
* T: the type of the Record to transform. | ||
@@ -363,2 +422,9 @@ */ | ||
/** | ||
* Shallowly transforms the keys of an Record to snake_case. | ||
* T: the type of the Record to transform. | ||
*/ | ||
type SnakeKeys<T> = T extends [] ? T : { | ||
[K in keyof T as SnakeCase<Is<K, string>>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to snake_case. The transformation is done both at runtime and type level. | ||
@@ -371,3 +437,10 @@ * @param obj the object to transform. | ||
/** | ||
* Transforms the keys of an Record to CONSTANT_CASE. | ||
* A strongly typed function that shallowly the keys of an object to snake_case. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @returns the transformed object. | ||
* @example snakeKeys({ 'foo-bar': { 'fizz-buzz': true } }) // { 'foo_bar': { 'fizz-buzz': true } } | ||
*/ | ||
declare function snakeKeys<T>(obj: T): SnakeKeys<T>; | ||
/** | ||
* Recursively transforms the keys of an Record to CONSTANT_CASE. | ||
* T: the type of the Record to transform. | ||
@@ -381,2 +454,9 @@ */ | ||
/** | ||
* Shallowly transforms the keys of an Record to CONSTANT_CASE. | ||
* T: the type of the Record to transform. | ||
*/ | ||
type ConstantKeys<T> = T extends [] ? T : { | ||
[K in keyof T as ConstantCase<Is<K, string>>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to CONSTANT_CASE. The transformation is done both at runtime and type level. | ||
@@ -389,3 +469,10 @@ * @param obj the object to transform. | ||
/** | ||
* Transforms the keys of an Record to a custom delimiter case. | ||
* A strongly typed function that shallowly transforms the keys of an object to CONSTANT_CASE. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @returns the transformed object. | ||
* @example constantKeys({ 'foo-bar': { 'fizz-buzz': true } }) // { FOO_BAR: { 'fizz-buzz': true } } | ||
*/ | ||
declare function constantKeys<T>(obj: T): ConstantKeys<T>; | ||
/** | ||
* Recursively transforms the keys of an Record to a custom delimiter case. | ||
* T: the type of the Record to transform. | ||
@@ -400,2 +487,10 @@ * D: the delimiter to use. | ||
/** | ||
* Shallowly transforms the keys of an Record to a custom delimiter case. | ||
* T: the type of the Record to transform. | ||
* D: the delimiter to use. | ||
*/ | ||
type DelimiterKeys<T, D extends string> = T extends [] ? T : { | ||
[K in keyof T as DelimiterCase<Is<K, string>, D>]: T[K]; | ||
}; | ||
/** | ||
* A strongly typed function that recursively transforms the keys of an object to a custom delimiter case. The transformation is done both at runtime and type level. | ||
@@ -408,3 +503,11 @@ * @param obj the object to transform. | ||
declare function deepDelimiterKeys<T, D extends string>(obj: T, delimiter: D): DeepDelimiterKeys<T, D>; | ||
/** | ||
* A strongly typed function that shallowly transforms the keys of an object to a custom delimiter case. The transformation is done both at runtime and type level. | ||
* @param obj the object to transform. | ||
* @param delimiter the delimiter to use. | ||
* @returns the transformed object. | ||
* @example delimiterKeys({ 'foo-bar': { 'fizz-buzz': true } }, '.') // { 'foo.bar': { 'fizz.buzz': true } } | ||
*/ | ||
declare function delimiterKeys<T, D extends string>(obj: T, delimiter: D): DelimiterKeys<T, D>; | ||
export { CamelCase, ConstantCase, DeepCamelKeys, DeepConstantKeys, DeepDelimiterKeys, DeepKebabKeys, DeepPascalKeys, DeepSnakeKeys, DelimiterCase, Digit, Is, IsDigit, IsLetter, IsLower, IsSeparator, IsSpecial, IsUpper, Join, KebabCase, PascalCase, Replace, ReplaceAll, Separator, SnakeCase, Split, TitleCase, Trim, TrimEnd, TrimStart, Words, capitalize, deepCamelKeys, deepConstantKeys, deepDelimiterKeys, deepKebabKeys, deepPascalKeys, deepSnakeKeys, deepTransformKeys, join, replace, replaceAll, split, toCamelCase, toConstantCase, toDelimiterCase, toKebabCase, toLowerCase, toPascalCase, toSnakeCase, toTitleCase, toUpperCase, trim, trimEnd, trimStart, words }; | ||
export { CamelCase, CamelKeys, CharAt, ConstantCase, ConstantKeys, DeepCamelKeys, DeepConstantKeys, DeepDelimiterKeys, DeepKebabKeys, DeepPascalKeys, DeepSnakeKeys, DelimiterCase, DelimiterKeys, Digit, Is, IsDigit, IsLetter, IsLower, IsSeparator, IsSpecial, IsUpper, Join, KebabCase, KebabKeys, PascalCase, PascalKeys, Replace, ReplaceAll, Separator, SnakeCase, SnakeKeys, Split, TitleCase, Trim, TrimEnd, TrimStart, Words, camelKeys, capitalize, charAt, constantKeys, deepCamelKeys, deepConstantKeys, deepDelimiterKeys, deepKebabKeys, deepPascalKeys, deepSnakeKeys, deepTransformKeys, delimiterKeys, join, kebabKeys, pascalKeys, replace, replaceAll, snakeKeys, split, toCamelCase, toConstantCase, toDelimiterCase, toKebabCase, toLowerCase, toPascalCase, toSnakeCase, toTitleCase, toUpperCase, trim, trimEnd, trimStart, words }; |
@@ -23,3 +23,6 @@ "use strict"; | ||
__export(src_exports, { | ||
camelKeys: () => camelKeys, | ||
capitalize: () => capitalize, | ||
charAt: () => charAt, | ||
constantKeys: () => constantKeys, | ||
deepCamelKeys: () => deepCamelKeys, | ||
@@ -32,5 +35,9 @@ deepConstantKeys: () => deepConstantKeys, | ||
deepTransformKeys: () => deepTransformKeys, | ||
delimiterKeys: () => delimiterKeys, | ||
join: () => join, | ||
kebabKeys: () => kebabKeys, | ||
pascalKeys: () => pascalKeys, | ||
replace: () => replace, | ||
replaceAll: () => replaceAll, | ||
snakeKeys: () => snakeKeys, | ||
split: () => split, | ||
@@ -54,2 +61,5 @@ toCamelCase: () => toCamelCase, | ||
// src/primitives.ts | ||
function charAt(str, index) { | ||
return str.charAt(index); | ||
} | ||
function join(tuple, delimiter) { | ||
@@ -136,17 +146,41 @@ return tuple.join(delimiter ?? ""); | ||
} | ||
function transformKeys(obj, transform) { | ||
if (typeOf(obj) !== "object") | ||
return obj; | ||
const res = {}; | ||
for (const key in obj) { | ||
res[transform(key)] = obj[key]; | ||
} | ||
return res; | ||
} | ||
function deepCamelKeys(obj) { | ||
return deepTransformKeys(obj, toCamelCase); | ||
} | ||
function camelKeys(obj) { | ||
return transformKeys(obj, toCamelCase); | ||
} | ||
function deepPascalKeys(obj) { | ||
return deepTransformKeys(obj, toPascalCase); | ||
} | ||
function pascalKeys(obj) { | ||
return transformKeys(obj, toPascalCase); | ||
} | ||
function deepKebabKeys(obj) { | ||
return deepTransformKeys(obj, toKebabCase); | ||
} | ||
function kebabKeys(obj) { | ||
return transformKeys(obj, toKebabCase); | ||
} | ||
function deepSnakeKeys(obj) { | ||
return deepTransformKeys(obj, toSnakeCase); | ||
} | ||
function snakeKeys(obj) { | ||
return transformKeys(obj, toSnakeCase); | ||
} | ||
function deepConstantKeys(obj) { | ||
return deepTransformKeys(obj, toConstantCase); | ||
} | ||
function constantKeys(obj) { | ||
return transformKeys(obj, toConstantCase); | ||
} | ||
function deepDelimiterKeys(obj, delimiter) { | ||
@@ -158,5 +192,11 @@ return deepTransformKeys( | ||
} | ||
function delimiterKeys(obj, delimiter) { | ||
return transformKeys(obj, (str) => toDelimiterCase(str, delimiter)); | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
camelKeys, | ||
capitalize, | ||
charAt, | ||
constantKeys, | ||
deepCamelKeys, | ||
@@ -169,5 +209,9 @@ deepConstantKeys, | ||
deepTransformKeys, | ||
delimiterKeys, | ||
join, | ||
kebabKeys, | ||
pascalKeys, | ||
replace, | ||
replaceAll, | ||
snakeKeys, | ||
split, | ||
@@ -174,0 +218,0 @@ toCamelCase, |
{ | ||
"name": "string-ts", | ||
"version": "0.5.1", | ||
"version": "1.0.0", | ||
"description": "Strongly-typed string functions.", | ||
@@ -31,7 +31,7 @@ "main": "./dist/index.js", | ||
"type": "git", | ||
"url": "git+https://github.com/gugaguichard/string-ts.git" | ||
"url": "git+https://github.com/gustavoguichard/string-ts.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/gugaguichard/string-ts/issues" | ||
"url": "https://github.com/gustavoguichard/string-ts/issues" | ||
} | ||
} |
392
README.md
@@ -9,4 +9,4 @@ # Strongly-typed string functions for all! | ||
```ts | ||
const str = 'hello-world' as const; | ||
const result = str.replace('-', ' '); // you should use: as 'hello world' | ||
const str = 'hello-world' | ||
const result = str.replace('-', ' ') // you should use: as 'hello world' | ||
// ^? string | ||
@@ -16,12 +16,62 @@ ``` | ||
## 🤓 The solution | ||
This library aims to solve this problem by providing a set of common functions that work with literal strings at both type and runtime level. | ||
```ts | ||
import { replace } from 'string-ts'; | ||
import { replace } from 'string-ts' | ||
const str = 'hello-world' as const; | ||
const result = replace(str, '-', ' '); | ||
const str = 'hello-world' | ||
const result = replace(str, '-', ' ') | ||
// ^ 'hello world' | ||
``` | ||
## 🔍 Why this matters | ||
TypeScript yields the best static analysis when types are highly specific. | ||
Literals are more specific than type `string`. | ||
This library preserves literals (and unions of literals) after transformations, unlike most existing utility libraries (and built-in string methods.) | ||
## In-depth example | ||
In the below example, I want to get a strongly-typed, camel-case version of `process.env`. | ||
One flow results in a loose type, and the other results in a more precise type. | ||
This example should illustrate the highly-specific and flexible nature of `string-ts`. | ||
_Note: All types in this example could be inferred, but are included for demonstrative purposes._ | ||
```ts | ||
import { deepCamelKeys } from 'string-ts' | ||
import { camelCase, mapKeys } from 'lodash-es' | ||
import type { Dictionary } from 'lodash' | ||
import z from 'zod' | ||
export const EnvSchema = z.object({ | ||
NODE_ENV: z.string(), | ||
}) | ||
function getEnvLoose() { | ||
const rawEnv: { NODE_ENV: string } = EnvSchema.parse(process.env) | ||
const env: Dictionary<string> = mapKeys(rawEnv, (_v, k) => camelCase(k)) | ||
// `Dictionary<string>` is too loose | ||
// TypeScript is okay with this, 'abc' will be of type `string` | ||
console.log(env.abc) | ||
} | ||
function getEnvPrecise() { | ||
const rawEnv: { NODE_ENV: string } = EnvSchema.parse(process.env) | ||
const env: { nodeEnv: string } = deepCamelKeys(rawEnv) | ||
// Error: Property 'abc' does not exist on type '{ nodeEnv: string; }' | ||
// Our type is more specific, so TypeScript catches this error. | ||
console.log(env.abc) | ||
} | ||
function main() { | ||
getEnvLoose() | ||
getEnvPrecise() | ||
} | ||
main() | ||
``` | ||
## 📦 Installation | ||
@@ -33,4 +83,6 @@ | ||
**** | ||
--- | ||
# 📖 API | ||
- [Runtime counterparts of native type utilities](#runtime-counterparts-of-native-type-utilities) | ||
@@ -44,2 +96,3 @@ - [capitalize](#capitalize) | ||
- [trimEnd](#trimend) | ||
- [chartAt](#charat) | ||
- [join](#join) | ||
@@ -58,2 +111,9 @@ - [replace](#replace) | ||
- [toTitleCase](#totitlecase) | ||
- [Strongly-typed shallow transformation of objects](#strongly-typed-shallow-transformation-of-objects) | ||
- [DelimiterKeys](#delimiterkeys) | ||
- [CamelKeys](#camelkeys) | ||
- [PascalKeys](#pascalkeys) | ||
- [KebabKeys](#kebabkeys) | ||
- [SnakeKeys](#snakekeys) | ||
- [ConstantKeys](#constantkeys) | ||
- [Strongly-typed deep transformation of objects](#strongly-typed-deep-transformation-of-objects) | ||
@@ -74,3 +134,3 @@ - [deepDelimiterKeys](#deepdelimiterkeys) | ||
**** | ||
--- | ||
@@ -80,9 +140,10 @@ ## Runtime counterparts of native type utilities | ||
### capitalize | ||
Capitalizes the first letter of a string. This is a runtime counterpart of `Capitalize<T>` from `src/types.d.ts`. | ||
```ts | ||
import { capitalize } from 'string-ts'; | ||
import { capitalize } from 'string-ts' | ||
const str = 'hello world' as const; | ||
const result = capitalize(str); | ||
const str = 'hello world' | ||
const result = capitalize(str) | ||
// ^ 'Hello world' | ||
@@ -92,10 +153,12 @@ ``` | ||
## Strongly-typed alternatives to native runtime utilities | ||
### toUpperCase | ||
This function is a strongly-typed counterpart of `String.prototype.toUpperCase`. | ||
```ts | ||
import { toUpperCase } from 'string-ts'; | ||
import { toUpperCase } from 'string-ts' | ||
const str = 'hello world' as const; | ||
const result = toUpperCase(str); | ||
const str = 'hello world' | ||
const result = toUpperCase(str) | ||
// ^ 'HELLO WORLD' | ||
@@ -105,9 +168,10 @@ ``` | ||
### toLowerCase | ||
This function is a strongly-typed counterpart of `String.prototype.toLowerCase`. | ||
```ts | ||
import { toLowerCase } from 'string-ts'; | ||
import { toLowerCase } from 'string-ts' | ||
const str = 'HELLO WORLD' as const; | ||
const result = toLowerCase(str); | ||
const str = 'HELLO WORLD' | ||
const result = toLowerCase(str) | ||
// ^ 'hello world' | ||
@@ -117,9 +181,10 @@ ``` | ||
### trim | ||
This function is a strongly-typed counterpart of `String.prototype.trim`. | ||
```ts | ||
import { trim } from 'string-ts'; | ||
import { trim } from 'string-ts' | ||
const str = ' hello world ' as const; | ||
const result = trim(str); | ||
const str = ' hello world ' | ||
const result = trim(str) | ||
// ^ 'hello world' | ||
@@ -129,9 +194,10 @@ ``` | ||
### trimStart | ||
This function is a strongly-typed counterpart of `String.prototype.trimStart`. | ||
```ts | ||
import { trimStart } from 'string-ts'; | ||
import { trimStart } from 'string-ts' | ||
const str = ' hello world ' as const; | ||
const result = trimStart(str); | ||
const str = ' hello world ' | ||
const result = trimStart(str) | ||
// ^ 'hello world ' | ||
@@ -141,20 +207,34 @@ ``` | ||
### trimEnd | ||
This function is a strongly-typed counterpart of `String.prototype.trimEnd`. | ||
```ts | ||
import { trimEnd } from 'string-ts'; | ||
import { trimEnd } from 'string-ts' | ||
const str = ' hello world ' as const; | ||
const result = trimEnd(str); | ||
const str = ' hello world ' | ||
const result = trimEnd(str) | ||
// ^ ' hello world' | ||
``` | ||
### charAt | ||
This function is a strongly-typed counterpart of `String.prototype.charAt`. | ||
```ts | ||
import { charAt } from 'string-ts' | ||
const str = 'hello world' | ||
const result = charAt(str, 6) | ||
// ^ 'w' | ||
``` | ||
### join | ||
This function is a strongly-typed counterpart of `Array.prototype.join`. | ||
```ts | ||
import { join } from 'string-ts'; | ||
import { join } from 'string-ts' | ||
const str = ['hello', 'world'] as ['hello', 'world']; | ||
const result = join(str, ' '); | ||
const str = ['hello', 'world'] as const | ||
const result = join(str, ' ') | ||
// ^ 'hello world' | ||
@@ -164,9 +244,10 @@ ``` | ||
### replace | ||
This function is a strongly-typed counterpart of `String.prototype.replace`. | ||
```ts | ||
import { replace } from 'string-ts'; | ||
import { replace } from 'string-ts' | ||
const str = 'hello-world-' as const; | ||
const result = replace(str, '-', ' '); | ||
const str = 'hello-world-' | ||
const result = replace(str, '-', ' ') | ||
// ^ 'hello world-' | ||
@@ -176,9 +257,10 @@ ``` | ||
### replaceAll | ||
This function is a strongly-typed counterpart of `String.prototype.replaceAll`. | ||
```ts | ||
import { replaceAll } from 'string-ts'; | ||
import { replaceAll } from 'string-ts' | ||
const str = 'hello-world-' as const; | ||
const result = replaceAll(str, '-', ' '); | ||
const str = 'hello-world-' | ||
const result = replaceAll(str, '-', ' ') | ||
// ^ 'hello world ' | ||
@@ -188,9 +270,10 @@ ``` | ||
### split | ||
This function is a strongly-typed counterpart of `String.prototype.split`. | ||
```ts | ||
import { split } from 'string-ts'; | ||
import { split } from 'string-ts' | ||
const str = 'hello-world' as const; | ||
const result = split(str, '-'); | ||
const str = 'hello-world' | ||
const result = split(str, '-') | ||
// ^ ['hello', 'world'] | ||
@@ -202,9 +285,10 @@ ``` | ||
### words | ||
This function identifies the words in a string and returns a tuple of words split by separators, differences in casing, numbers, and etc. | ||
```ts | ||
import { words } from 'string-ts'; | ||
import { words } from 'string-ts' | ||
const str = '-20someVery-weird String' as const; | ||
const result = words(str); | ||
const str = '-20someVery-weird String' | ||
const result = words(str) | ||
// ^ ['20', 'some', 'Very', 'weird', 'String'] | ||
@@ -214,9 +298,10 @@ ``` | ||
### toDelimiterCase | ||
This function converts a string to a new case with a custom delimiter at both runtime and type levels. | ||
```ts | ||
import { toDelimiterCase } from 'string-ts'; | ||
import { toDelimiterCase } from 'string-ts' | ||
const str = 'helloWorld' as const; | ||
const result = toDelimiterCase(str, '.'); | ||
const str = 'helloWorld' | ||
const result = toDelimiterCase(str, '.') | ||
// ^ 'hello.World' | ||
@@ -226,9 +311,10 @@ ``` | ||
### toCamelCase | ||
This function converts a string to `camelCase` at both runtime and type levels. | ||
```ts | ||
import { toCamelCase } from 'string-ts'; | ||
import { toCamelCase } from 'string-ts' | ||
const str = 'hello-world' as const; | ||
const result = toCamelCase(str); | ||
const str = 'hello-world' | ||
const result = toCamelCase(str) | ||
// ^ 'helloWorld' | ||
@@ -238,9 +324,10 @@ ``` | ||
### toPascalCase | ||
This function converts a string to `PascalCase` at both runtime and type levels. | ||
```ts | ||
import { toPascalCase } from 'string-ts'; | ||
import { toPascalCase } from 'string-ts' | ||
const str = 'hello-world' as const; | ||
const result = toPascalCase(str); | ||
const str = 'hello-world' | ||
const result = toPascalCase(str) | ||
// ^ 'HelloWorld' | ||
@@ -250,9 +337,10 @@ ``` | ||
### toKebabCase | ||
This function converts a string to `kebab-case` at both runtime and type levels. | ||
```ts | ||
import { toKebabCase } from 'string-ts'; | ||
import { toKebabCase } from 'string-ts' | ||
const str = 'helloWorld' as const; | ||
const result = toKebabCase(str); | ||
const str = 'helloWorld' | ||
const result = toKebabCase(str) | ||
// ^ 'hello-world' | ||
@@ -262,9 +350,10 @@ ``` | ||
### toSnakeCase | ||
This function converts a string to `snake_case` at both runtime and type levels. | ||
```ts | ||
import { toSnakeCase } from 'string-ts'; | ||
import { toSnakeCase } from 'string-ts' | ||
const str = 'helloWorld' as const; | ||
const result = toSnakeCase(str); | ||
const str = 'helloWorld' | ||
const result = toSnakeCase(str) | ||
// ^ 'hello_world' | ||
@@ -274,9 +363,10 @@ ``` | ||
### toConstantCase | ||
This function converts a string to `CONSTANT_CASE` at both runtime and type levels. | ||
```ts | ||
import { toConstantCase } from 'string-ts'; | ||
import { toConstantCase } from 'string-ts' | ||
const str = 'helloWorld' as const; | ||
const result = toConstantCase(str); | ||
const str = 'helloWorld' | ||
const result = toConstantCase(str) | ||
// ^ 'HELLO_WORLD' | ||
@@ -286,19 +376,113 @@ ``` | ||
### toTitleCase | ||
This function converts a string to `Title Case` at both runtime and type levels. | ||
```ts | ||
import { toTitleCase } from 'string-ts'; | ||
import { toTitleCase } from 'string-ts' | ||
const str = 'helloWorld' as const; | ||
const result = toTitleCase(str); | ||
const str = 'helloWorld' | ||
const result = toTitleCase(str) | ||
// ^ 'Hello World' | ||
``` | ||
## Strongly-typed shallow transformation of objects | ||
### delimiterKeys | ||
This function shallowly converts the keys of an object to a new case with a custom delimiter at both runtime and type levels. | ||
```ts | ||
import { delimiterKeys } from 'string-ts'; | ||
const data = { | ||
'hello-world': { | ||
'foo-bar': 'baz', | ||
}, | ||
} as const; | ||
const result = delimiterKeys(data, '.'); | ||
// ^ { 'hello.world': { 'foo-bar': 'baz' } } | ||
``` | ||
### camelKeys | ||
This function shallowly converts the keys of an object to `camelCase` at both runtime and type levels. | ||
```ts | ||
import { camelKeys } from 'string-ts'; | ||
const data = { | ||
'hello-world': { | ||
'foo-bar': 'baz', | ||
}, | ||
} as const; | ||
const result = camelKeys(data); | ||
// ^ { helloWorld: { 'foo-bar': 'baz' } } | ||
``` | ||
### pascalKeys | ||
This function shallowly converts the keys of an object to `PascalCase` at both runtime and type levels. | ||
```ts | ||
import { pascalKeys } from 'string-ts'; | ||
const data = { | ||
'hello-world': { | ||
'foo-bar': 'baz', | ||
}, | ||
} as const; | ||
const result = pascalKeys(data); | ||
// ^ { HelloWorld: { FooBar: 'baz' } } | ||
``` | ||
### kebabKeys | ||
This function shallowly converts the keys of an object to `kebab-case` at both runtime and type levels. | ||
```ts | ||
import { kebabKeys } from 'string-ts'; | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
}, | ||
} as const; | ||
const result = kebabKeys(data); | ||
// ^ { 'hello-world': { fooBar: 'baz' } } | ||
``` | ||
### snakeKeys | ||
This function shallowly converts the keys of an object to `snake_case` at both runtime and type levels. | ||
```ts | ||
import { snakeKeys } from 'string-ts'; | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
}, | ||
} as const; | ||
const result = snakeKeys(data); | ||
// ^ { 'hello_world': { 'fooBar': 'baz' } } | ||
``` | ||
### constantKeys | ||
This function shallowly converts the keys of an object to `CONSTANT_CASE` at both runtime and type levels. | ||
```ts | ||
import { constantKeys } from 'string-ts'; | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
}, | ||
} as const; | ||
const result = constantKeys(data); | ||
// ^ { 'HELLO_WORLD': { 'fooBar': 'baz' } } | ||
``` | ||
## Strongly-typed deep transformation of objects | ||
### deepDelimiterKeys | ||
This function recursively converts the keys of an object to a new case with a custom delimiter at both runtime and type levels. | ||
```ts | ||
import { deepDelimiterKeys } from 'string-ts'; | ||
import { deepDelimiterKeys } from 'string-ts' | ||
@@ -309,4 +493,4 @@ const data = { | ||
}, | ||
} as const; | ||
const result = deepDelimiterKeys(data, '.'); | ||
} as const | ||
const result = deepDelimiterKeys(data, '.') | ||
// ^ { 'hello.world': { 'foo.bar': 'baz' } } | ||
@@ -316,6 +500,7 @@ ``` | ||
### deepCamelKeys | ||
This function recursively converts the keys of an object to `camelCase` at both runtime and type levels. | ||
```ts | ||
import { deepCamelKeys } from 'string-ts'; | ||
import { deepCamelKeys } from 'string-ts' | ||
@@ -326,4 +511,4 @@ const data = { | ||
}, | ||
} as const; | ||
const result = deepCamelKeys(data); | ||
} as const | ||
const result = deepCamelKeys(data) | ||
// ^ { helloWorld: { fooBar: 'baz' } } | ||
@@ -333,6 +518,7 @@ ``` | ||
### deepPascalKeys | ||
This function recursively converts the keys of an object to `PascalCase` at both runtime and type levels. | ||
```ts | ||
import { deepPascalKeys } from 'string-ts'; | ||
import { deepPascalKeys } from 'string-ts' | ||
@@ -343,4 +529,4 @@ const data = { | ||
}, | ||
} as const; | ||
const result = deepPascalKeys(data); | ||
} as const | ||
const result = deepPascalKeys(data) | ||
// ^ { HelloWorld: { FooBar: 'baz' } } | ||
@@ -350,13 +536,14 @@ ``` | ||
### deepKebabKeys | ||
This function recursively converts the keys of an object to `kebab-case` at both runtime and type levels. | ||
```ts | ||
import { deepKebabKeys } from 'string-ts'; | ||
import { deepKebabKeys } from 'string-ts' | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
helloWorld: { | ||
fooBar: 'baz', | ||
}, | ||
} as const; | ||
const result = deepKebabKeys(data); | ||
} as const | ||
const result = deepKebabKeys(data) | ||
// ^ { 'hello-world': { 'foo-bar': 'baz' } } | ||
@@ -366,13 +553,14 @@ ``` | ||
### deepSnakeKeys | ||
This function recursively converts the keys of an object to `snake_case` at both runtime and type levels. | ||
```ts | ||
import { deepSnakeKeys } from 'string-ts'; | ||
import { deepSnakeKeys } from 'string-ts' | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
helloWorld: { | ||
fooBar: 'baz', | ||
}, | ||
} as const; | ||
const result = deepSnakeKeys(data); | ||
} as const | ||
const result = deepSnakeKeys(data) | ||
// ^ { 'hello_world': { 'foo_bar': 'baz' } } | ||
@@ -382,13 +570,14 @@ ``` | ||
### deepConstantKeys | ||
This function recursively converts the keys of an object to `CONSTANT_CASE` at both runtime and type levels. | ||
```ts | ||
import { deepConstantKeys } from 'string-ts'; | ||
import { deepConstantKeys } from 'string-ts' | ||
const data = { | ||
'helloWorld': { | ||
'fooBar': 'baz', | ||
helloWorld: { | ||
fooBar: 'baz', | ||
}, | ||
} as const; | ||
const result = deepConstantKeys(data); | ||
} as const | ||
const result = deepConstantKeys(data) | ||
// ^ { 'HELLO_WORLD': { 'FOO_BAR': 'baz' } } | ||
@@ -398,9 +587,11 @@ ``` | ||
## Type Utilities | ||
All the functions presented in this API have associated type counterparts. | ||
```ts | ||
import type * as St from 'string-ts'; | ||
import type * as St from 'string-ts' | ||
``` | ||
### Native TS type utilities | ||
```ts | ||
@@ -413,4 +604,6 @@ Capitalize<'hello world'> // 'Hello world' | ||
### General Type utilities from this library | ||
```ts | ||
St.Words<'hello-world'> // ['hello', 'world'] | ||
St.CharAt<'hello world', 6> // 'w' | ||
St.Join<['hello', 'world'], '-'> // 'hello-world' | ||
@@ -426,2 +619,3 @@ St.Replace<'hello-world', 'l', '1'> // 'he1lo-world' | ||
### Casing type utilities | ||
```ts | ||
@@ -436,5 +630,8 @@ St.CamelCase<'hello-world'> // 'helloWorld' | ||
St.DeepDelimiterKeys<{ | ||
'hello-world': { 'foo-bar': 'baz' } | ||
}, '.'> // { 'hello.world': { 'foo.bar': 'baz' } } | ||
St.DeepDelimiterKeys< | ||
{ | ||
'hello-world': { 'foo-bar': 'baz' } | ||
}, | ||
'.' | ||
> // { 'hello.world': { 'foo.bar': 'baz' } } | ||
St.DeepCamelKeys<{ | ||
@@ -447,9 +644,9 @@ 'hello-world': { 'foo-bar': 'baz' } | ||
St.DeepKebabKeys<{ | ||
'helloWorld': { 'fooBar': 'baz' } | ||
helloWorld: { fooBar: 'baz' } | ||
}> // { 'hello-world': { 'foo-bar': 'baz' } } | ||
St.DeepSnakeKeys<{ | ||
'helloWorld': { 'fooBar': 'baz' } | ||
helloWorld: { fooBar: 'baz' } | ||
}> // { 'hello_world': { 'foo_bar': 'baz' } } | ||
St.DeepConstantKeys<{ | ||
'helloWorld': { 'fooBar': 'baz' } | ||
helloWorld: { fooBar: 'baz' } | ||
}> // { 'HELLO_WORLD': { 'FOO_BAR': 'baz' } } | ||
@@ -459,2 +656,3 @@ ``` | ||
### Other exported type utilities | ||
```ts | ||
@@ -478,17 +676,19 @@ St.IsDigit<'a'> // false | ||
## Runtime-only utilities | ||
### deepTransformKeys | ||
This function recursively converts the keys of an object to a custom format, but only at runtime level. | ||
```ts | ||
import { deepTransformKeys, toUpperCase } from 'string-ts'; | ||
import { deepTransformKeys, toUpperCase } from 'string-ts' | ||
const data = { 'helloWorld': 'baz' } as const; | ||
const data = { helloWorld: 'baz' } as const | ||
type MyType<T> = { [K in keyof T as Uppercase<K>]: T[K] } | ||
const result = deepTransformKeys(data, toUpperCase) as MyType<typeof data>; | ||
const result = deepTransformKeys(data, toUpperCase) as MyType<typeof data> | ||
// ^ { 'HELLOWORLD': 'baz' } | ||
``` | ||
## Disclaimer | ||
## Disclaimer | ||
This library doesn't support every internal character for the sake of keeping the maintainer's sanity. |
Sorry, the diff of this file is not supported yet
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
50401
878
0
1
657