Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add string case reified types #96

Merged
merged 14 commits into from
Oct 12, 2024
Prev Previous commit
Next Next commit
feat: add snake-case string method
  • Loading branch information
poteat committed Oct 12, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 683dfd59354a456fb3d6e6e11117ec217a03bb42
1 change: 1 addition & 0 deletions src/string/index.ts
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@ export * from './prepend'
export * from './replace'
export * from './reverse'
export * from './slice'
export * from './snake-case'
export * from './split'
export * from './starts-with'
export * from './tail'
43 changes: 43 additions & 0 deletions src/string/snake-case.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { $, Test, String } from '..'

type SnakeCase_Spec = [
/**
* Can convert a string to snake_case.
*/
Test.Expect<$<String.SnakeCase, 'hello world'>, 'hello_world'>,

/**
* Can convert a string with multiple words.
*/
Test.Expect<$<String.SnakeCase, 'hello world 42'>, 'hello_world_42'>,

/**
* Can convert a string with acronyms.
*/
Test.Expect<$<String.SnakeCase, 'XMLHttpRequest'>, 'xml_http_request'>,

/**
* Can convert a string with numbers.
*/
Test.Expect<$<String.SnakeCase, 'hello42world'>, 'hello_42_world'>
]

it('should convert a string to snake_case', () => {
expect(String.snakeCase('hello world')).toBe('hello_world')
})

it('should convert a string with multiple words', () => {
expect(String.snakeCase('hello world 42')).toBe('hello_world_42')
})

it('should convert a string with acronyms', () => {
expect(String.snakeCase('XMLHttpRequest')).toBe('xml_http_request')
})

it('should convert a string with numbers', () => {
expect(String.snakeCase('hello42world')).toBe('hello_42_world')
})

it('should handle an empty string', () => {
expect(String.snakeCase('')).toBe('')
})
62 changes: 62 additions & 0 deletions src/string/snake-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Kind, Type, String as String_ } from '..'

/**
* `_$snakeCase` is a type-level function that takes in a string `S` and returns
* a new string in the "snake_case" format, whereby each word is lowercased and
* separated by underscores.
*
* @template {string} S - The string to convert.
*
* @example
* ```ts
* import { String } from "hkt-toolbelt";
*
* type Result = String._$snakeCase<'hello world'>; // 'hello_world'
* ```
*/
export type _$snakeCase<
S extends string,
WORDS extends string[] = String_._$words<S>
> = String_._$join<
{
[K in keyof WORDS]: String_._$toLower<WORDS[K]>
},
'_'
>

/**
* `SnakeCase` is a type-level function that takes in a string `S` and returns
* a new string in the "snake_case" format, whereby each word is lowercased and
* separated by underscores.
*
* @template {string} S - The string to convert.
*
* @example
* ```ts
* import { $, String } from "hkt-toolbelt";
*
* type Result = $<String.SnakeCase, 'hello world'>; // 'hello_world'
* ```
*/
export interface SnakeCase extends Kind.Kind {
f(x: Type._$cast<this[Kind._], string>): _$snakeCase<typeof x>
}

/**
* Given a string, return a new string in the "snake_case" format, whereby each
* word is lowercased and separated by underscores.
*
* @param {string} x - The string to convert.
*
* @example
* ```ts
* import { String } from "hkt-toolbelt";
*
* const result = String.snakeCase('hello world')
* // ^? 'hello_world'
* ```
*/
export const snakeCase = ((x: string) =>
String_.words(x)
.map((word) => word.toLowerCase())
.join('_')) as Kind._$reify<SnakeCase>