Open olivierbeaulieu opened 2 years ago
@olivierbeaulieu I ran into this same issue.
I absolutely love this library, but I really do feel like char->number transitions should add underscore as well. It might be reasonable to not make this assumption, especially with something like { "http2": true } where we would NOT want http2 -> http_2. There is no clear standard on this but the most common approach is "column_1" rather than "column1" if you are doing everything in snake case...
Here are some Frankenstein'd types.... That being said this is not nearly as elegant as the type definitions written in this library, so idk.
Snake To Camel Case:
export type CamelizeInputType = Record<PropertyKey, any> | Array<any>;
export type SnakeToCamelCase<S extends PropertyKey> = S extends number
? S
: S extends `${infer T}_${infer U}`
? `${T}${Capitalize<SnakeToCamelCase<U>>}`
: S;
export type SnakeToCamelCaseNested<T> = T extends Function | RegExp | Date
? T
: T extends (infer E)[]
? SnakeToCamelCaseNested<E>[]
: T extends CamelizeInputType
? {
[K in keyof T as SnakeToCamelCase<Extract<K, PropertyKey>>]: SnakeToCamelCaseNested<T[K]>;
}
: T;
Camel To Snake Case
export type SnakeCaseInputType = Record<PropertyKey, any> | Array<any>;
type UpperAlphabetic =
| '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 AlphanumericDigits = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
/**
* Return underscore if it is allowed between provided characters,
* trail and lead underscore are allowed, empty string is considered
* as the beginning of a string.
*/
type SnakeUnderscore<
First extends PropertyKey,
Second extends PropertyKey
> = First extends AlphanumericDigits
? Second extends UpperAlphabetic
? '_'
: ''
: First extends UpperAlphabetic | '' | '_'
? ''
: Second extends UpperAlphabetic | AlphanumericDigits
? '_'
: '';
/**
* Convert string literal type to snake_case
*/
type CamelToSnakeCase<S extends PropertyKey, Previous extends PropertyKey = ''> = S extends number
? S
: S extends `${infer First}${infer Second}${infer Rest}`
? `${SnakeUnderscore<Previous, First>}${Lowercase<First>}${SnakeUnderscore<
First,
Second
>}${Lowercase<Second>}${CamelToSnakeCase<Rest, First>}`
: S extends `${infer First}`
? `${SnakeUnderscore<Previous, First>}${Lowercase<First>}`
: '';
edit: lodash won't 100% match these types. they are close but not all there.
Would love some more references on how this is dealt with in other projects.
That a snake cased foo_bar_1
should become fooBar1
camel cased is obvious.
That a camel cased fooBar1
should become foo_bar_1
or foo_bar1
is not as obvious to me.
foo_bar1
and foo_bar_1
both gets converted to fooBar1
when camel cased and hence only one of them can be converted the reverse way without an issue. Fixing foo_bar_1
will break foo_bar1
and as thus is potentially quite the breaking change.
Kind of relates to #224 and #488 in that both of those also refer to lodash
and how lodash does things in regards to this
foo_bar1
andfoo_bar_1
both gets converted tofooBar1
when camel cased and hence only one of them can be converted the reverse way without an issue. Fixingfoo_bar_1
will breakfoo_bar1
and as thus is potentially quite the breaking change.
@fregante Are we sure its a bug? Would still want some references here that indicates that camel cased fooBar1
should become foo_bar_1
rather than foo_bar1
Feel free to change the label. It looked like a bug report when I fast-triaged 80 issues in the repo 😅
Given the following example:
I would expect each operation to be the opposite operation of each other - resulting in F being back to its original value
F = { foo_1: boolean }
.That is not the current behaviour, we as have
F = { foo1: boolean }
.Is that behaviour correct? If so, how can I get back to my original value when jumping from one case to the other?
For context, I'm trying to match the result of lodash's snakeCase
Upvote & Fund