https://github.com/toss/es-toolkit/pull/1362
type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
: Lowercase<S>;
type ToCamelCaseKeys<T> = T extends any[]
? Array<ToCamelCaseKeys<T[number]>>
: T extends Record<string, any>
? { [K in keyof T as CamelCase<string & K>]: ToCamelCaseKeys<T[K]> }
: T;
type CamelCase를 보면 snake_case만 고려가 되어 있고 PascalCase는 고려가 되어 있지 않는 걸 확인할 수 있다.
type SnakeToCamel<S extends string> = S extends `${infer H}_${infer T}`
? `${Lowercase<H>}${Capitalize<SnakeToCamel<T>>}`
: Lowercase<S>;
type PascalToCamel<S extends string> = S extends `${infer F}${infer R}` ? `${Lowercase<F>}${R}` : S;
/** If it's snake_case, apply the snake_case rule; otherwise, just lowercase the first letter (including PascalCase → camelCase). */
type AnyToCamel<S extends string> = S extends `${string}_${string}` ? SnakeToCamel<S> : PascalToCamel<S>;
type ToCamelCaseKeys<T> = T extends any[]
? Array<ToCamelCaseKeys<T[number]>>
: T extends Record<string, any>
? { [K in keyof T as AnyToCamel<Extract<K, string>>]: ToCamelCaseKeys<T[K]> }
: T;
PascalCase를 고려하도록 함.
infer
는 조건부 타입(conditional type) 안에서 새로운 타입 변수를 선언할 때 사용함.
보통 extends
와 함께 쓰여서, 타입의 일부분을 추출(infer = 추론)하는 역할을 함.
런타임 코드가 아니라 타입 레벨에서 "이 부분을 타입 변수로 잡아서 써라"라고 함.
예시
type GetFirstArg<T> = T extends (arg: infer U, ...args: any[]) => any ? U : never;
type A = GetFirstArg<(x: number, y: string) => void>; // number
type B = GetFirstArg<(x: boolean) => void>; // boolean
👉 함수 타입에서 첫 번째 인자의 타입을 U
로 추론해내는 패턴.
타입스크립트가 제공하는 내장 유틸리티 타입 중 하나.
문자열 리터럴 타입의 모든 문자를 소문자로 변환해줌.
런타임 문자열이 아니라 타입 레벨에서만 작동한다는 게 포인트.
예시
type A = Lowercase<'HELLO'>; // "hello"
type B = Lowercase<'UserName'>; // "username"
이것도 내장 유틸리티 타입.
문자열 리터럴 타입의 첫 글자만 대문자로 바꿔줌. 나머지는 그대로 둠.
예시
type A = Capitalize<'hello'>; // "Hello"
type B = Capitalize<'userName'>; // "UserName"