Last active
January 8, 2024 21:23
-
-
Save mykeels/c5cded0a63397a4992567573c8c697f6 to your computer and use it in GitHub Desktop.
Change cashing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { camelCase, pascalCase, snakeCase } from "change-case"; | |
type CamelToPascalCase<S extends string> = S extends `${infer F}${infer R}` | |
? `${Capitalize<F>}${R}` | |
: S; | |
type PascalToCamelCase<S extends string> = S extends `${infer F}${infer R}` | |
? `${Uncapitalize<F>}${R}` | |
: S; | |
type SnakeToCamelCase<S extends string> = S extends `${infer F}_${infer R}` | |
? `${Uncapitalize<F>}${Capitalize<SnakeToCamelCase<R>>}` | |
: S; | |
type CamelToSnakeCase<S extends string> = S extends `${infer F}${infer R}` | |
? `${Uncapitalize<F>}${R extends Capitalize<R> | |
? `_${Uncapitalize<R>}` : CamelToSnakeCase<R>}` | |
: S; | |
type StringKeys<TEntity extends {}> = { | |
[key in keyof TEntity]: key extends string ? key : never; | |
}[keyof TEntity]; | |
type IsLiteral<TValue> = TValue extends string | number | boolean | symbol | |
? true | |
: false; | |
declare module "change-case" { | |
export function camelCase<TInput extends string>( | |
input: TInput | |
): SnakeToCamelCase<PascalToCamelCase<TInput>>; | |
export function snakeCase<TInput extends string>( | |
input: TInput | |
): CamelToSnakeCase<TInput>; | |
export function pascalCase<TInput extends string>( | |
input: TInput | |
): CamelToPascalCase<TInput>; | |
} | |
type Known<T, K> = unknown extends T ? unknown extends K ? never : K : T; | |
export type ToCamel<TInput> = TInput extends string | |
? SnakeToCamelCase<PascalToCamelCase<TInput>> | |
: IsLiteral<TInput> extends true | |
? TInput | |
: TInput extends Array<infer TItem> | |
? Array<ToCamel<TItem>> | |
: TInput extends { [key: string]: any } | |
? { | |
[key in SnakeToCamelCase<PascalToCamelCase<StringKeys<TInput>>>]: IsLiteral< | |
Known<TInput[CamelToSnakeCase<key>], TInput[CamelToPascalCase<key>]> | |
> extends true | |
? Known<TInput[CamelToSnakeCase<key>], TInput[CamelToPascalCase<key>]> | |
: ToCamel<Known<TInput[CamelToSnakeCase<key>], TInput[CamelToPascalCase<key>]>>; | |
} | |
: TInput; | |
/** | |
* Converts a string, the keys of an object, or keys of an array of objects to camelCase. | |
* | |
* @example | |
* toCamel('HelloWorld') // 'helloWorld' | |
* toCamel({ HelloWorld: 'Hello World' }) // { helloWorld: 'Hello World' } | |
* toCamel([{ HelloWorld: 'Hello World' }]) // [{ helloWorld: 'Hello World' }] | |
*/ | |
export function toCamel<TInput>( | |
input: TInput | |
): TInput extends null | undefined ? TInput : ToCamel<TInput> { | |
if (!input) { | |
return input as any; | |
} else if (Array.isArray(input)) { | |
return input.map((i) => toCamel(i)) as any; | |
} else if (typeof input === "string") { | |
return camelCase(input) as any; | |
} else if (typeof input === "object") { | |
return Object.keys(input).reduce((result, key) => { | |
const literalTypes = ["string", "number", "boolean", "symbol"]; | |
const value = (input as Record<string, any>)[key]; | |
result[camelCase(key)] = literalTypes.includes(typeof value) | |
? value | |
: toCamel(value); | |
return result; | |
}, {} as Record<string, any>) as any; | |
} else { | |
return input as any; | |
} | |
} | |
type ToPascal<TInput> = TInput extends string | |
? CamelToPascalCase<TInput> | |
: IsLiteral<TInput> extends true | |
? TInput | |
: TInput extends Array<infer TItem> | |
? Array<ToPascal<TItem>> | |
: TInput extends { [key: string]: any } | |
? { | |
[key in CamelToPascalCase<StringKeys<TInput>>]: IsLiteral< | |
TInput[PascalToCamelCase<key>] | |
> extends true | |
? TInput[PascalToCamelCase<key>] | |
: ToPascal<TInput[PascalToCamelCase<key>]>; | |
} | |
: TInput; | |
/** | |
* Converts a string, the keys of an object, or keys of an array of objects to PascalCase. | |
* | |
* @example | |
* toPascal('helloWorld') // 'HelloWorld' | |
* toPascal({ helloWorld: 'Hello World' }) // { HelloWorld: 'Hello World' } | |
* toPascal([{ helloWorld: 'Hello World' }]) // [{ HelloWorld: 'Hello World' }] | |
*/ | |
export function toPascal<TInput>( | |
input: TInput | |
): TInput extends null | undefined ? TInput : ToPascal<TInput> { | |
if (!input) { | |
return input as any; | |
} else if (Array.isArray(input)) { | |
return input.map((i) => toPascal(i)) as any; | |
} else if (typeof input === "string") { | |
return pascalCase(input) as any; | |
} else if (typeof input === "object") { | |
return Object.keys(input).reduce((result, key) => { | |
const literalTypes = ["string", "number", "boolean", "symbol"]; | |
const value = (input as Record<string, any>)[key]; | |
result[pascalCase(key)] = literalTypes.includes(typeof value) | |
? value | |
: toPascal(value); | |
return result; | |
}, {} as Record<string, any>) as any; | |
} else { | |
return input as any; | |
} | |
} | |
export type ToSnake<TInput> = TInput extends string | |
? CamelToSnakeCase<TInput> | |
: IsLiteral<TInput> extends true | |
? TInput | |
: TInput extends Array<infer TItem> | |
? Array<ToSnake<TItem>> | |
: TInput extends { [key: string]: any } | |
? { | |
[key in CamelToSnakeCase<StringKeys<TInput>>]: IsLiteral< | |
TInput[SnakeToCamelCase<key>] | |
> extends true | |
? TInput[SnakeToCamelCase<key>] | |
: ToSnake<TInput[CamelToSnakeCase<key>]>; | |
} | |
: TInput; | |
/** | |
* Converts a string, the keys of an object, or keys of an array of objects to snakeCase. | |
* | |
* @example | |
* toSnake('HelloWorld') // 'hello_world' | |
* toSnake({ HelloWorld: 'Hello World' }) // { hello_world: 'Hello World' } | |
* toSnake([{ helloWorld: 'Hello World' }]) // [{ hello_world: 'Hello World' }] | |
*/ | |
export function toSnake<TInput>( | |
input: TInput | |
): TInput extends null | undefined ? TInput : ToSnake<TInput> { | |
if (!input) { | |
return input as any; | |
} else if (Array.isArray(input)) { | |
return input.map((i) => toSnake(i)) as any; | |
} else if (typeof input === "string") { | |
return snakeCase(input) as any; | |
} else if (typeof input === "object") { | |
return Object.keys(input).reduce((result, key) => { | |
const literalTypes = ["string", "number", "boolean", "symbol"]; | |
const value = (input as Record<string, any>)[key]; | |
result[snakeCase(key)] = literalTypes.includes(typeof value) | |
? value | |
: toSnake(value); | |
return result; | |
}, {} as Record<string, any>) as any; | |
} else { | |
return input as any; | |
} | |
} | |
type Expect<T extends true> = T; | |
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y | |
? 1 | |
: 2 | |
? true | |
: false; | |
type cases = [ | |
// Camel | |
Expect<Equal<ToCamel<"HelloWorld">, "helloWorld">>, | |
Expect<Equal<ToCamel<"hello_world">, "helloWorld">>, | |
Expect< | |
Equal<ToCamel<{ HelloWorld: "Hello World" }>, { helloWorld: "Hello World" }> | |
>, | |
Expect< | |
Equal<ToCamel<{ hello_world: "Hello World" }>, { helloWorld: "Hello World" }> | |
>, | |
Expect< | |
Equal< | |
ToCamel<[{ HelloWorld: "Hello World" }]>[0], | |
{ helloWorld: "Hello World" } | |
> | |
>, | |
Expect< | |
Equal< | |
ToCamel<[{ hello_world: "Hello World" }]>[0], | |
{ helloWorld: "Hello World" } | |
> | |
>, | |
Expect< | |
Equal< | |
ToCamel<[{ HelloWorld: "Hello World" }, { HelloAfrica: "Hello Africa" }]>, | |
( | |
| { | |
helloWorld: "Hello World"; | |
} | |
| { | |
helloAfrica: "Hello Africa"; | |
} | |
)[] | |
> | |
>, | |
Expect< | |
Equal< | |
ToCamel<[{ hello_world: "Hello World" }, { hello_africa: "Hello Africa" }]>, | |
( | |
| { | |
helloWorld: "Hello World"; | |
} | |
| { | |
helloAfrica: "Hello Africa"; | |
} | |
)[] | |
> | |
>, | |
// Pascal | |
Expect<Equal<ToPascal<"helloWorld">, "HelloWorld">>, | |
Expect< | |
Equal< | |
ToPascal<{ helloWorld: "Hello World" }>, | |
{ HelloWorld: "Hello World" } | |
> | |
>, | |
Expect< | |
Equal< | |
ToPascal<[{ helloWorld: "Hello World" }]>[0], | |
{ HelloWorld: "Hello World" } | |
> | |
>, | |
Expect< | |
Equal< | |
ToPascal< | |
[{ helloWorld: "Hello World" }, { helloAfrica: "Hello Africa" }] | |
>, | |
( | |
| { | |
HelloWorld: "Hello World"; | |
} | |
| { | |
HelloAfrica: "Hello Africa"; | |
} | |
)[] | |
> | |
>, | |
// Snake | |
Expect<Equal<ToSnake<"helloWorld">, "hello_world">>, | |
Expect<Equal<ToSnake<"HelloWorld">, "hello_world">>, | |
Expect< | |
Equal<ToSnake<{ helloWorld: "Hello World" }>, { hello_world: "Hello World" }> | |
>, | |
Expect< | |
Equal< | |
ToSnake<[{ helloWorld: "Hello World" }]>[0], | |
{ hello_world: "Hello World" } | |
> | |
>, | |
Expect< | |
Equal< | |
ToSnake<[{ helloWorld: "Hello World" }, { helloAfrica: "Hello Africa" }]>, | |
( | |
| { | |
hello_world: "Hello World"; | |
} | |
| { | |
hello_africa: "Hello Africa"; | |
} | |
)[] | |
> | |
>, | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment