deep-case-crafter
Version:
Transforms deeply nested object, array, Map, and Set keys between common case formats while preserving TypeScript type safety
280 lines (279 loc) • 16.7 kB
TypeScript
import { Expand } from './helperTypes';
import { StringCase } from './stringCaseTypes';
import { TransformOptions } from './transformOptionTypes';
/**
* Type utility that transforms a snake_case string to camelCase
* Example: 'hello_world' -> 'helloWorld'
*/
export type SnakeToCamel<S extends string> = S extends `${infer P}_${infer C}${infer R}` ? `${P}${Uppercase<C>}${SnakeToCamel<R>}` : S;
/**
* Type utility that transforms a snake_case string to PascalCase
* Example: 'hello_world' -> 'HelloWorld'
*/
type SnakeWordsToPascal<Parts extends string[]> = Parts extends [
infer First extends string,
...infer Rest extends string[]
] ? `${Capitalize<First>}${SnakeWordsToPascal<Rest>}` : '';
type SnakeToWords<S extends string> = S extends `${infer P}_${infer R}` ? [P, ...SnakeToWords<R>] : [S];
export type SnakeToPascal<S extends string> = S extends `${infer P}_${infer R}` ? SnakeWordsToPascal<[P, ...SnakeToWords<R>]> : Capitalize<S>;
/**
* Type utility that transforms a snake_case string to kebab-case
* Example: 'hello_world' -> 'hello-world'
*/
export type SnakeToKebab<S extends string> = S extends `${infer P}_${infer R}` ? `${P}-${SnakeToKebab<R>}` : S;
/**
* Type utility that transforms a camelCase string to snake_case
* Example: 'helloWorld' -> 'hello_world'
*/
export type CamelToSnake<S extends string> = S extends `${infer C}${infer R}` ? C extends Uppercase<C> ? `_${Lowercase<C>}${CamelToSnake<R>}` : `${C}${CamelToSnake<R>}` : S;
/**
* Type utility that transforms a camelCase string to PascalCase
* Example: 'helloWorld' -> 'HelloWorld'
*/
export type CamelToPascal<S extends string> = Capitalize<S>;
/**
* Type utility that transforms a camelCase string to kebab-case
* Example: 'helloWorld' -> 'hello-world'
*/
export type CamelToKebab<S extends string> = S extends `${infer C}${infer R}` ? C extends Uppercase<C> ? `-${Lowercase<C>}${CamelToKebab<R>}` : `${C}${CamelToKebab<R>}` : S;
/**
* Type utility that transforms a PascalCase string to camelCase
* Example: 'HelloWorld' -> 'helloWorld'
*/
export type PascalToCamel<S extends string> = S extends `${infer F}${infer R}` ? `${Lowercase<F>}${R}` : S;
/**
* Type utility that transforms a PascalCase string to snake_case
* Example: 'HelloWorld' -> 'hello_world'
*/
export type PascalToSnake<S extends string> = S extends `${infer F}${infer R}` ? R extends `` ? Lowercase<F> : `${Lowercase<F>}${PascalToSnakeInner<R>}` : S;
type PascalToSnakeInner<S extends string> = S extends `${infer C}${infer R}` ? C extends Uppercase<C> ? `_${Lowercase<C>}${PascalToSnakeInner<R>}` : `${C}${PascalToSnakeInner<R>}` : S;
/**
* Type utility that transforms a PascalCase string to kebab-case
* Example: 'HelloWorld' -> 'hello-world'
*/
export type PascalToKebab<S extends string> = S extends `${infer F}${infer R}` ? R extends `` ? Lowercase<F> : `${Lowercase<F>}${PascalToKebabInner<R>}` : S;
type PascalToKebabInner<S extends string> = S extends `${infer C}${infer R}` ? C extends Uppercase<C> ? `-${Lowercase<C>}${PascalToKebabInner<R>}` : `${C}${PascalToKebabInner<R>}` : S;
/**
* Type utility that transforms a kebab-case string to camelCase
* Example: 'hello-world' -> 'helloWorld'
*/
export type KebabToCamel<S extends string> = S extends `${infer P}-${infer C}${infer R}` ? `${P}${Uppercase<C>}${KebabToCamel<R>}` : S;
/**
* Type utility that transforms a kebab-case string to PascalCase
* Example: 'hello-world' -> 'HelloWorld'
*/
type KebabWordsToPascal<Parts extends string[]> = Parts extends [
infer First extends string,
...infer Rest extends string[]
] ? `${Capitalize<First>}${KebabWordsToPascal<Rest>}` : '';
type KebabToWords<S extends string> = S extends `${infer P}-${infer R}` ? [P, ...KebabToWords<R>] : [S];
/**
* Type utility that transforms a kebab-case string to PascalCase
* Example: 'hello-world' -> 'HelloWorld'
*/
export type KebabToPascal<S extends string> = S extends `${infer P}-${infer R}` ? KebabWordsToPascal<[P, ...KebabToWords<R>]> : Capitalize<S>;
/**
* Type utility that transforms a kebab-case string to snake_case
* Example: 'hello-world' -> 'hello_world'
*/
export type KebabToSnake<S extends string> = S extends `${infer P}-${infer R}` ? `${P}_${KebabToSnake<R>}` : S;
/**
* Utility type to detect preserved keys that shouldn't be transformed.
* Preserved keys include those with leading/trailing special characters,
* unsupported patterns, or invalid starting/ending characters.
*/
type ForbiddenStartChars = '_' | '-' | '$' | '&' | '%' | '#' | '@' | '!' | '*' | '+' | '=' | '~' | '`' | '^' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '\\' | ':' | ';' | '"' | "'" | '<' | '>' | ',' | '.' | '?' | '/';
type ForbiddenEndChars = '_' | '-' | '$' | '&' | '%' | '#' | '@' | '!' | '*' | '+' | '=' | '~' | '`' | '^' | '(' | ')' | '[' | ']' | '{' | '}' | '|' | '\\' | ':' | ';' | '"' | "'" | '<' | '>' | ',' | '.' | '?' | '/' | `${number}`;
type PreservedKey<K extends string> = K extends `${infer First}${string}` ? First extends ForbiddenStartChars ? true : K extends `${string}${infer Last}` ? Last extends ForbiddenEndChars ? true : K extends `${string}__${string}` | `${string}--${string}` ? true : false : false : false;
/**
* Type utility that selects the correct string transformation based on source and target casing
*/
export type TransformKey<K extends string, O extends TransformOptions> = PreservedKey<K> extends true ? K : undefined extends O['sourceCase'] ? string : O['sourceCase'] extends 'snake' ? undefined extends O['targetCase'] ? SnakeToCamel<K> : O['targetCase'] extends 'camel' ? SnakeToCamel<K> : O['targetCase'] extends 'pascal' ? SnakeToPascal<K> : O['targetCase'] extends 'kebab' ? SnakeToKebab<K> : K : O['sourceCase'] extends 'camel' ? undefined extends O['targetCase'] ? K : O['targetCase'] extends 'snake' ? CamelToSnake<K> : O['targetCase'] extends 'pascal' ? CamelToPascal<K> : O['targetCase'] extends 'kebab' ? CamelToKebab<K> : K : O['sourceCase'] extends 'pascal' ? undefined extends O['targetCase'] ? PascalToCamel<K> : O['targetCase'] extends 'camel' ? PascalToCamel<K> : O['targetCase'] extends 'snake' ? PascalToSnake<K> : O['targetCase'] extends 'kebab' ? PascalToKebab<K> : K : O['sourceCase'] extends 'kebab' ? undefined extends O['targetCase'] ? KebabToCamel<K> : O['targetCase'] extends 'camel' ? KebabToCamel<K> : O['targetCase'] extends 'pascal' ? KebabToPascal<K> : O['targetCase'] extends 'snake' ? KebabToSnake<K> : K : K;
/**
* Type utility that transforms all keys in an object from the source case to the target case
*/
export type TransformObjectKeys<T, O extends TransformOptions = {
targetCase: 'camel';
}> = {
[K in keyof T as TransformKey<K & string, O>]: T[K];
};
/**
* Helper type to determine the return type of transformObject based on options
*/
export type TransformObjectReturn<T, O> = O extends {
sourceCase: StringCase;
} ? Expand<TransformObjectKeys<T, O>> : Record<string | number | symbol, unknown>;
/**
* Helper type to determine the return type of transformMap based on options
*/
export type TransformMapReturn<K, V, O> = O extends {
sourceCase: StringCase;
} ? Map<K extends string ? TransformKey<K & string, O> : K, V> : Map<string | Exclude<K, string>, V>;
/**
* Object transformation type aliases
* These provide more readable type names for common transformations
*/
export type SnakeToCamelObject<T> = TransformObjectKeys<T, {
sourceCase: 'snake';
targetCase: 'camel';
}>;
export type SnakeToPascalObject<T> = TransformObjectKeys<T, {
sourceCase: 'snake';
targetCase: 'pascal';
}>;
export type SnakeToKebabObject<T> = TransformObjectKeys<T, {
sourceCase: 'snake';
targetCase: 'kebab';
}>;
export type CamelToSnakeObject<T> = TransformObjectKeys<T, {
sourceCase: 'camel';
targetCase: 'snake';
}>;
export type CamelToPascalObject<T> = TransformObjectKeys<T, {
sourceCase: 'camel';
targetCase: 'pascal';
}>;
export type CamelToKebabObject<T> = TransformObjectKeys<T, {
sourceCase: 'camel';
targetCase: 'kebab';
}>;
export type PascalToCamelObject<T> = TransformObjectKeys<T, {
sourceCase: 'pascal';
targetCase: 'camel';
}>;
export type PascalToSnakeObject<T> = TransformObjectKeys<T, {
sourceCase: 'pascal';
targetCase: 'snake';
}>;
export type PascalToKebabObject<T> = TransformObjectKeys<T, {
sourceCase: 'pascal';
targetCase: 'kebab';
}>;
export type KebabToCamelObject<T> = TransformObjectKeys<T, {
sourceCase: 'kebab';
targetCase: 'camel';
}>;
export type KebabToPascalObject<T> = TransformObjectKeys<T, {
sourceCase: 'kebab';
targetCase: 'pascal';
}>;
export type KebabToSnakeObject<T> = TransformObjectKeys<T, {
sourceCase: 'kebab';
targetCase: 'snake';
}>;
/**
* Array transformation type aliases
*/
export type SnakeToCamelArray<T> = Array<SnakeToCamelObject<T>>;
export type SnakeToPascalArray<T> = Array<SnakeToPascalObject<T>>;
export type SnakeToKebabArray<T> = Array<SnakeToKebabObject<T>>;
export type CamelToSnakeArray<T> = Array<CamelToSnakeObject<T>>;
export type CamelToPascalArray<T> = Array<CamelToPascalObject<T>>;
export type CamelToKebabArray<T> = Array<CamelToKebabObject<T>>;
export type PascalToCamelArray<T> = Array<PascalToCamelObject<T>>;
export type PascalToSnakeArray<T> = Array<PascalToSnakeObject<T>>;
export type PascalToKebabArray<T> = Array<PascalToKebabObject<T>>;
export type KebabToCamelArray<T> = Array<KebabToCamelObject<T>>;
export type KebabToPascalArray<T> = Array<KebabToPascalObject<T>>;
export type KebabToSnakeArray<T> = Array<KebabToSnakeObject<T>>;
/**
* Generic transformation utility for objects
*/
export type TransformObject<T, From extends StringCase, To extends StringCase> = TransformObjectKeys<T, {
sourceCase: From;
targetCase: To;
}>;
/**
* Generic transformation utility for arrays
*/
export type TransformArray<T, From extends StringCase, To extends StringCase> = Array<TransformObject<T, From, To>>;
/**
* Helper type to determine array return type based on options
*/
export type TransformArrayReturn<T extends unknown[], O> = O extends {
sourceCase: StringCase;
} ? {
[K in keyof T]: T[K] extends object ? TransformObjectKeys<T[K], O> : T[K] extends Map<infer KT, infer VT> ? Map<KT extends string ? string : KT, VT> : T[K];
} : unknown[];
/**
* Recursive type transformations
* These types handle nested structures like arrays, objects, Maps, and Sets
*/
export type SnakeToCamelRecursive<T> = T extends Array<infer U> ? Array<SnakeToCamelRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? SnakeToCamel<K> : K, SnakeToCamelRecursive<V>> : T extends Set<infer V> ? Set<SnakeToCamelRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'snake';
targetCase: 'camel';
}>]: SnakeToCamelRecursive<T[K]>;
} : T;
export type SnakeToPascalRecursive<T> = T extends Array<infer U> ? Array<SnakeToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? SnakeToPascal<K> : K, SnakeToPascalRecursive<V>> : T extends Set<infer V> ? Set<SnakeToPascalRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'snake';
targetCase: 'pascal';
}>]: SnakeToPascalRecursive<T[K]>;
} : T;
export type SnakeToKebabRecursive<T> = T extends Array<infer U> ? Array<SnakeToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? SnakeToKebab<K> : K, SnakeToKebabRecursive<V>> : T extends Set<infer V> ? Set<SnakeToKebabRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'snake';
targetCase: 'kebab';
}>]: SnakeToKebabRecursive<T[K]>;
} : T;
export type CamelToSnakeRecursive<T> = T extends Array<infer U> ? Array<CamelToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToSnake<K> : K, CamelToSnakeRecursive<V>> : T extends Set<infer V> ? Set<CamelToSnakeRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'camel';
targetCase: 'snake';
}>]: CamelToSnakeRecursive<T[K]>;
} : T;
export type CamelToPascalRecursive<T> = T extends Array<infer U> ? Array<CamelToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToPascal<K> : K, CamelToPascalRecursive<V>> : T extends Set<infer V> ? Set<CamelToPascalRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'camel';
targetCase: 'pascal';
}>]: CamelToPascalRecursive<T[K]>;
} : T;
export type CamelToKebabRecursive<T> = T extends Array<infer U> ? Array<CamelToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? CamelToKebab<K> : K, CamelToKebabRecursive<V>> : T extends Set<infer V> ? Set<CamelToKebabRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'camel';
targetCase: 'kebab';
}>]: CamelToKebabRecursive<T[K]>;
} : T;
export type PascalToCamelRecursive<T> = T extends Array<infer U> ? Array<PascalToCamelRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToCamel<K> : K, PascalToCamelRecursive<V>> : T extends Set<infer V> ? Set<PascalToCamelRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'pascal';
targetCase: 'camel';
}>]: PascalToCamelRecursive<T[K]>;
} : T;
export type PascalToSnakeRecursive<T> = T extends Array<infer U> ? Array<PascalToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToSnake<K> : K, PascalToSnakeRecursive<V>> : T extends Set<infer V> ? Set<PascalToSnakeRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'pascal';
targetCase: 'snake';
}>]: PascalToSnakeRecursive<T[K]>;
} : T;
export type PascalToKebabRecursive<T> = T extends Array<infer U> ? Array<PascalToKebabRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? PascalToKebab<K> : K, PascalToKebabRecursive<V>> : T extends Set<infer V> ? Set<PascalToKebabRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'pascal';
targetCase: 'kebab';
}>]: PascalToKebabRecursive<T[K]>;
} : T;
export type KebabToCamelRecursive<T> = T extends Array<infer U> ? Array<KebabToCamelRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToCamel<K> : K, KebabToCamelRecursive<V>> : T extends Set<infer V> ? Set<KebabToCamelRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'kebab';
targetCase: 'camel';
}>]: KebabToCamelRecursive<T[K]>;
} : T;
export type KebabToPascalRecursive<T> = T extends Array<infer U> ? Array<KebabToPascalRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToPascal<K> : K, KebabToPascalRecursive<V>> : T extends Set<infer V> ? Set<KebabToPascalRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'kebab';
targetCase: 'pascal';
}>]: KebabToPascalRecursive<T[K]>;
} : T;
export type KebabToSnakeRecursive<T> = T extends Array<infer U> ? Array<KebabToSnakeRecursive<U>> : T extends Map<infer K, infer V> ? Map<K extends string ? KebabToSnake<K> : K, KebabToSnakeRecursive<V>> : T extends Set<infer V> ? Set<KebabToSnakeRecursive<V>> : T extends object ? {
[K in keyof T as TransformKey<K & string, {
sourceCase: 'kebab';
targetCase: 'snake';
}>]: KebabToSnakeRecursive<T[K]>;
} : T;
/**
* Generic recursive transformation type
* Allows dynamically selecting the transformation based on source and target cases
*/
export type TransformRecursive<T, FromCase extends StringCase, ToCase extends StringCase> = FromCase extends 'snake' ? ToCase extends 'camel' ? SnakeToCamelRecursive<T> : ToCase extends 'pascal' ? SnakeToPascalRecursive<T> : ToCase extends 'kebab' ? SnakeToKebabRecursive<T> : T : FromCase extends 'camel' ? ToCase extends 'snake' ? CamelToSnakeRecursive<T> : ToCase extends 'pascal' ? CamelToPascalRecursive<T> : ToCase extends 'kebab' ? CamelToKebabRecursive<T> : T : FromCase extends 'pascal' ? ToCase extends 'snake' ? PascalToSnakeRecursive<T> : ToCase extends 'camel' ? PascalToCamelRecursive<T> : ToCase extends 'kebab' ? PascalToKebabRecursive<T> : T : FromCase extends 'kebab' ? ToCase extends 'snake' ? KebabToSnakeRecursive<T> : ToCase extends 'camel' ? KebabToCamelRecursive<T> : ToCase extends 'pascal' ? KebabToPascalRecursive<T> : T : T;
export {};