01383-hard-camelize

Back

type Capital<T extends string> = T extends `${infer R}${infer rest}` ? R extends '_' ? Capital<Capitalize<rest>> : `${R}${Capital<rest>}` : T
type Camelize<T> = T extends any[] ? (T extends [infer R, ...infer rest] ? [Camelize<R>, ...Camelize<rest>] : []) :
T extends object ? {
  [K in keyof T as K extends string ? Capital<K> : K]:  Camelize<T[K]>
} : T

Solution by ouzexi #34276

type Camelize<T extends object> = {
  [P in keyof T as CamelizeString<P & string>]: T[P] extends any[]
    ? CamelizeArr<T[P]>
    : T[P] extends object
    ? Camelize<T[P]>
    : T[P];
};

type CamelizeString<S extends string> = S extends `${infer Pre}_${infer Rest}`
  ? `${Pre}${CamelizeString<Capitalize<Rest>>}`
  : S;
type CamelizeArr<T extends any[]> = T extends [infer L, ...infer R]
  ? [Camelize<L & object>, ...CamelizeArr<R>]
  : [];

Solution by Vampirelee #32630

type SnakeToCamel<T extends string, R extends string = ''> = T extends `${infer F}_${infer L}`
  ? SnakeToCamel<Capitalize<L>, `${R}${F}`>
  : `${R}${T}`

type Camelize<T extends object> = T extends any[]
  ? { [K in keyof T]: T[K] extends object ? Camelize<T[K]> : T[K] }
  : { [K in keyof T as SnakeToCamel<K & string>]: T[K] extends object ? Camelize<T[K]> : T[K] }

Solution by Heonys #32392

type CamelCase<S extends string> = S extends `${infer L}_${infer R1}${infer R2}`
  ? Uppercase<R1> extends Lowercase<R1>
    ? `${Lowercase<L>}_${CamelCase<`${R1}${R2}`>}`
    : `${Lowercase<L>}${Uppercase<R1>}${CamelCase<R2>}`
  : Lowercase<S>;


type Camelize<T> = T extends any[]
  ? { [K in keyof T]: Camelize<T[K]> }
  : keyof T extends never
  ? T
  : { [K in keyof T as CamelCase<K & string>]: Camelize<T[K]> }; 

Solution by vangie #32242

// 解答をここに記入
type SnakeToCamel<T> = T extends `${infer F}_${infer Rest}` ? `${F}${SnakeToCamel<Capitalize<Rest>>}` : T
type Camelize<T> = T extends unknown[]
  ? { [P in keyof T]: Camelize<T[P]>}
  : T extends object
    ? { [P in keyof T as SnakeToCamel<P> ] : Camelize<T[P]>}
    : T

SnakeToCamel<T>は、snake_case を camelCase に変換します。 (愚直にやると以下のような実装などが考えられますが、上記のようにCapitalize を使うと楽です。)

type SnakeToCamel<T, ShouldBeUpperCase = false> = T extends `${infer F}${infer Rest}` ? F extends "_" ? SnakeToCamel<Rest, true> : `${ShouldBeUpperCase extends true ? Uppercase<F> : F}${SnakeToCamel<Rest>}` : ''

Camelize については、object の各 key を、as で camelCase に変換する動作を再帰的に行えばいいので、

type Camelize<T> = 
  T extends object
    ? { [P in keyof T as SnakeToCamel<P> ] : Camelize<T[P]>}
    : T

が基本の形となります。

しかし、これだと、今回のテストケースにある array のような配列に対応できないので、

type cases = [
  Expect<Equal<
    Camelize<{
      some_prop: string
      prop: { another_prop: string }

     // ここに対応できない
      array: [
        { snake_case: string },
        { another_element: { yet_another_prop: string } },
        { yet_another_element: string },
      ]
    }>,
    {
      someProp: string
      prop: { anotherProp: string }
     // ここに対応できない
      array: [
        { snakeCase: string },
        { anotherElement: { yetAnotherProp: string } },
        { yetAnotherElement: string },
      ]
    }
  >>,
]

配列の場合だけ、自身の key を変換せずに子に再帰を繋いであげる必要があります。 よって最終的な解答は

type Camelize<T> = T extends unknown[]
  ? { [P in keyof T]: Camelize<T[P]>}
  : T extends object
    ? { [P in keyof T as SnakeToCamel<P> ] : Camelize<T[P]>}
    : T

のようになります。

Solution by Kakeru-Miyazaki #30967

type UnderscoreToCamelCase<T extends string> = 
  T extends `${infer A}_${infer B}${infer C}` 
  ? UnderscoreToCamelCase<`${A}${Capitalize<B>}${C}`> 
  : T

type CamelizeArray<T extends unknown[]> = T extends [infer First extends Record<string, any>, ...infer Rest] 
  ? [Camelize<First>, ...CamelizeArray<Rest>] 
  : []

type Camelize<T extends Record<string, any>> = {
  [K in keyof T as K extends string ? UnderscoreToCamelCase<K> : K]: T[K] extends unknown[] 
  ? CamelizeArray<T[K]> 
  : T[K] extends object ? Camelize<T[K]> : T[K]
}

Solution by jjswifty #30894

type CamelizeKey<T extends string> = T extends `${infer F}_${infer C}${infer R}` ? `${F}${Capitalize<C>}${CamelizeKey<R>}` : T 

type ForEach<T extends Object[]> = T extends [infer First,...infer Rest] ? [Camelize<First>,...ForEach<Rest extends Object[] ? Rest : never>] : []

type Camelize<T extends any> = T extends Object[] ? ForEach<T>:  T extends Object ? {
    [Key in keyof T as CamelizeKey<Key extends string ? Key : never>]: Camelize<T[Key]> 
} : T

Solution by idebbarh #30778

// your answers

type ToCame<T> = T extends `${infer First}_${infer Rest}` ? ToCame<`${First}${Capitalize<Rest>}`> : T

type DoActionAry<T extends unknown[]> = T extends [infer First, ...infer Rest] ? [Camelize<First>, ...DoActionAry<Rest>]  : []

type Camelize<T> = T extends object ? {
  [Key in keyof T as ToCame<Key>]: T[Key] extends any[] ? DoActionAry<T[Key]> : T[Key] extends object ? Camelize<T[Key]> : T[Key]
} : T

Solution by 437204933 #29701

// your answers

type ToCame<T> = T extends `${infer First}_${infer Rest}` ? ToCame<`${First}${Capitalize<Rest>}`> : T

type DoActionAry<T extends unknown[]> = T extends [infer First, ...infer Rest] ? [Camelize<First>, ...DoActionAry<Rest>]  : []

type Camelize<T> = T extends object ? {
  [Key in keyof T as ToCame<Key>]: T[Key] extends any[] ? DoActionAry<T[Key]> : T[Key] extends object ? Camelize<T[Key]> : T[Key]
} : T

type GetPath<SPath extends string> = SPath extends `${infer Key}.${infer Rest}` ? [Key, ...GetPath<Rest>] : [SPath]

type DeepPick<Obj extends object, SPath extends string> = UnionToEx<FindObj<Obj, GetPath<SPath>>

Solution by 437204933 #29700

[1383]

type CamelCase<S> = S extends `${infer P}_${infer W}` ? `${P}${CamelCase<Capitalize<W>>}` : S;

type FormatArrayItem<T extends any[]> = T extends [infer P, ...infer W] ? [Camelize<P>, ...FormatArrayItem<W>] : T;

type FormatObjectItem<T extends Record<any, any>> = {
  [P in keyof T as P extends string ? CamelCase<P> : P]: T[P] extends Record<any, any> ? Camelize<T[P]> : T[P];
};

export type Camelize<T extends any[] | Record<any, any>> = T extends any[]
  ? FormatArrayItem<T>
  : T extends Record<any, any>
  ? FormatObjectItem<T>
  : T;

Solution by fakestudy #29447

// 你的答案
type convertUnderscoreToCamelCase<T> = T extends `${infer F}_${infer R}` ? `${F}${Capitalize<convertUnderscoreToCamelCase<R>>}` : T

type TraverseTuple<T extends any[], Result extends any[] = []> = T extends [infer Head extends Record<string, any>, ...infer Rest]
  ? TraverseTuple<Rest, [...Result, Camelize<Head>]>
  : Result;

type test = convertUnderscoreToCamelCase<'ws_wzc'>

type Camelize<T extends Record<string, any>> = {[k in keyof T as convertUnderscoreToCamelCase<k>]:
    T[k] extends Record<string, any> ? 
      T[k] extends any[] ? TraverseTuple<T[k]> :
      Camelize<T[k]> 
      : T[k]
  }

Solution by WangZiChu199910252255 #29084

The as CamelizeStr<K> clause should have no effect for tuple types where K is 0, 1, etc. But it makes the mapped type non-homomorphic, which means that it prevents tuples and arrays from coming through as tuples and arrays. Hence the check for extends any[] and a homomorphic mapped type (i.e. without the as) in that case.

Using a homomorphic mapped type has some nice knock-on properties like preserving the labels on tuple elements, which #1403 does not.

type CamelizeStr<T extends PropertyKey> =
  T extends `${infer Start}_${infer Rest}`
    ? `${Start}${Capitalize<CamelizeStr<Rest>>}`
    : T

type Camelize<T> = T extends any[]
  ? { [K in keyof T]: Camelize<T[K]> }
  : {
      [K in keyof T as CamelizeStr<K>]: Camelize<T[K]>
    }

Example of preserving labels:

type T = Camelize<{
  some_prop: string
  prop: { another_prop: string }
  array: [
    el1: { snake_case: string },
    el2: { readonly another_element: { yet_another_prop: string } },
    el3: { yet_another_element: string }
  ]
}>
image

Solution by danvk #28685

type IsAlphabet<T extends string> = Lowercase<T> extends Uppercase<T>
  ? false
  : true

type CamelCase<S extends string> = S extends `${infer X}_${infer Y}${infer Z}`
  // If Y is a symbol (not alphabetic) then keep keep that in and don't change it
  ? IsAlphabet<Y> extends false
    ? `${Lowercase<X>}_${CamelCase<`${Y}${Z}`>}`
    // Else capitalize and append it
    : `${Lowercase<X>}${Uppercase<Y>}${CamelCase<Z>}`
  : Lowercase<S>

type Camelize<T> = T extends unknown[]
  ? { [K in keyof T]: T[K] extends object ? Camelize<T[K]> : T[K] }
  : { [K in keyof T as CamelCase<K & string>]: T[K] extends object ? Camelize<T[K]> : T[K] }

Solution by HubooDeclan #27063

type SnakeToCamel<S> = S extends `${infer L}_${infer U}${infer R}`
  ? `${L}${Uppercase<U>}${SnakeToCamel<R>}`
  : S;

type CamelizeArray<T> = T extends [infer F, ...infer R]
  ? [Camelize<F>, ...CamelizeArray<R>]
  : [];

type Camelize<T> = {
  [K in keyof T as SnakeToCamel<K>]: T[K] extends unknown[]
    ? CamelizeArray<T[K]>
    : T[K] extends object
    ? Camelize<T[K]>
    : T[K];
};

Solution by JohnLi1999 #25783

type CamelizeOne<T extends string> = T extends `${infer P}_${infer R}` ? `${P}${Capitalize<CamelizeOne<R>>}` : T;
type CamelizeArray<T extends any[]> = T extends [infer F, ...infer R] ? [F extends object ? Camelize<F> : F, ...CamelizeArray<R>] : [];   //数组

type Camelize<T extends object> = T extends any[] ? CamelizeArray<T> :
  { [P in keyof T as P extends string ? CamelizeOne<P> : P]: T[P] extends object ? Camelize<T[P]> : T[P] };

// old way
// /**驼峰命名 */
// type CamelizeKey<T extends string> = T extends `${infer L}_${infer RF}${infer R}` ? `${L}${Uppercase<RF>}${CamelizeKey<R>}` : T;

// type Camelize<T extends object> = T extends any[] ?
//   { [P in keyof T]: P extends `${number}`/*only index elements*/ ? (T[P] extends object ? Camelize<T[P]> : T[P]) : T[P] } :   //数组
//   { [P in keyof T as P extends string ? CamelizeKey<P> : P]: T[P] extends object ? Camelize<T[P]> : T[P] };                   //结构体

Solution by E-uler #24962

type SnakeToCamelCase<S extends string> = S extends `${infer SFirst}_${infer SRest}` 
  ? `${Lowercase<SFirst>}${SnakeToCamelCase.Rest<SRest>}` : Lowercase<S>
namespace SnakeToCamelCase {
  export type Rest<S extends string> = S extends `${infer SCurrent}_${infer SRest}` 
    ? `${Capitalize<SCurrent>}${Rest<SRest>}` : Capitalize<S>
}

type Camelize<T> = T extends unknown[] ? Camelize.FromArray<T> : (
  T extends Record<string, unknown> ? Camelize.FromObject<T> : T
)
namespace Camelize {
  export type FromObject<T extends Record<string, unknown>> = {
    [Key in keyof T as Key extends string ? SnakeToCamelCase<Key> : Key]: Camelize<T[Key]>
  } & {}

  export type FromArray<T extends unknown[]> = T extends [infer T0, ...infer TRest] ? (
    [Camelize<T0>, ...FromArray<TRest>]
  ) : T extends [] ? [] : Camelize<T[number]>[]
}

Solution by BOCbMOU #24851

type CamelCase<S extends string> = S extends `${infer First}_${infer Rest}`
? `${First}${CamelCase<Capitalize<Rest>>}`
: S 

type CamelizeArray<T> = T extends [infer F, ...infer Rest] 
? [Camelize<F>, ...CamelizeArray<Rest>] 
: []

type Camelize<T> = {
  [Key in keyof T as Key extends string ? CamelCase<Key> : never ]: T[Key] extends any[]
    ? CamelizeArray<T[Key]>
    : T[Key] extends object 
      ? Camelize<T[Key]>
      : T[Key]
}

Solution by NeylonR #24450

// your answers
type CamelCase<S extends string> = S extends `${infer F}_${infer M}${infer E}` ? 
`${Lowercase<F>}${Uppercase<M>}${CamelCase<E>}` : Lowercase<S>;

type CamelizeArray<T> = T extends [infer F, ...infer Rest] ? [Camelize<F>, ...CamelizeArray<Rest>] : []; 

type Camelize<T> = {
  [key in keyof T as key extends string ? CamelCase<key>: never]: T[key] extends any[] ? 
  CamelizeArray<T[key] > : T[key]  extends object ? Camelize<T[key]> : T[key]
}

Solution by snakeUni #23836

type IsTuple<T> = T extends any[] ? (number extends T['length'] ? false : true) : false

type CamelizeTuple<T> = T extends [infer F, ...infer R]
	? R['length'] extends 0
		? [Camelize<F>]
		: [Camelize<F>, ...CamelizeTuple<R>]
	: [T]

type Camelize<T> = IsTuple<T> extends true
	? CamelizeTuple<T>
	: T extends object
	? {
			[K in keyof T as K extends `${infer F}_${infer R}`
				? `${F}${Capitalize<R>}`
				: K]: T[K] extends object ? Camelize<T[K]> : T[K]
    }
	: T

Solution by TKBnice #23319

type CamelCase<S extends string> = S extends `${infer F}_${infer M}${infer E}` ? 
`${Lowercase<F>}${Uppercase<M>}${CamelCase<E>}` : Lowercase<S>;
type CamelizeArray<T> = T extends [infer F, ...infer Rest] ? [Camelize<F>, ...CamelizeArray<Rest>] : []; 

type Camelize<T> = {
  [key in keyof T as key extends string ? CamelCase<key>: never]: T[key] extends any[] ? 
  CamelizeArray<T[key] > : T[key]  extends object ? Camelize<T[key]> : T[key]
}

Solution by Karamuto #22037

type STOC<S extends string> = S extends `${infer A}_${infer B}` ? `${A}${Capitalize<STOC<B>>}` : S

type Camelize<T> = T extends any[]
    ? T extends [infer A, ... infer B] ? [Camelize<A>, ...Camelize<B>] : []
    : {
        [K in keyof T as K extends string ? STOC<K> : never]:
        T[K] extends { [_: keyof any]: any } ? Camelize<T[K]> : T[K]
    }

Solution by goddnsgit #21913

type CamelizeString<T extends PropertyKey> = T extends `${infer First}_${infer Last}`
  ? `${First}${CamelizeString<Capitalize<Last>>}`
  : T;

type Camelize<T> = T extends any[]
  ? { [K in keyof T]: Camelize<T[K]> }
  : T extends Record<PropertyKey, any>
  ? {
      [K in keyof T as CamelizeString<K>]: Camelize<T[K]>;
    }
  : T;

type T1 = Camelize<{
  some_prop_prop2__a: string;
  prop: { another_prop: string };
  array: [{ snake_case: string; snake_b: number }];
}>;

Solution by zqiangxu #21826

type CamelizeString<S extends string> = 
  S extends `${infer Head}${infer Rest}`
    ? Head extends '_'
      ? CamelizeString<Capitalize<Rest>>
      : `${Head}${CamelizeString<Rest>}`
    : S

type Camelize<T> = 
  T extends unknown[]
    ? T extends [infer First, ...infer Rest]
      ? [Camelize<First>, ...Camelize<Rest>]
      : []
    : T extends object
      ? {
        [P in keyof T as (P extends string ? CamelizeString<P> : P)]: Camelize<T[P]>
      }
      : T

playground

Solution by zhaoyao91 #21348

// your answers
type ToCamelize<S> = S extends `${infer F}_${infer R}` ? 
`${F}${Capitalize<R>}` extends `${infer F1}_${infer R1}` ? 
ToCamelize<`${F1}_${R1}`> : `${F}${Capitalize<R>}` : S

type Camelize<T> = {
  [K in keyof T as ToCamelize<K>] : T[K] extends any[] ? ProcessArr<T[K]> : T[K] extends object ? Camelize<T[K]> : T[K]
}

type ProcessArr<A,N = []> = A extends [infer F,...infer R] ? F extends object ? N extends [...infer Rest] ? ProcessArr<R,[...Rest,Camelize<F>]> : N : N : N

Solution by YqxLzx #21317

type StringToUnion<S> = S extends `${infer F}${infer R}`
  ? F | StringToUnion<R>
  : S;
type CapitalizeCamelCase<
  S extends string,
  R extends string = ""
> = S extends `${infer A}_${infer B}`
  ? CapitalizeCamelCase<B, `${R}${Capitalize<A>}`>
  : `${R}${Capitalize<S>}`;

type CamelCase<S extends string> = "_" extends StringToUnion<S>
  ? Uncapitalize<CapitalizeCamelCase<Lowercase<S>>>
  : Lowercase<S>;
type ArrCamelizeDeep<T, Result extends Array<any> = []> = T extends [
  infer A,
  ...infer B
]
  ? ArrCamelizeDeep<B, [...Result, Camelize<A>]>
  : Result;

type Camelize<T> = {
  [P in keyof T as P extends string ? CamelCase<P> : P]: T[P] extends Array<any>
    ? ArrCamelizeDeep<T[P]>
    : T[P] extends Record<string, any>
    ? Camelize<T[P]>
    : T[P];
};

Solution by so11y #21234

// your answers
type _CamelizeKey<P> = P extends `${infer F}_${infer C}${infer Rest}`
  ? `${F}${Uppercase<C>}${_CamelizeKey<Rest>}`
  : P;

type Camelize<T> = T extends Record<string, unknown>
  ? { [P in keyof T as _CamelizeKey<P>]: Camelize<T[P]> }
  : T extends [infer F, ...infer Rest]
  ? [Camelize<F>, ...Camelize<Rest>]
  : T;

Solution by fengjinlong #20260

type _CamelizeHelp<P> =
  P extends `${ infer F }_${ infer C }${ infer Rest }`
    ? `${ F }${ Uppercase<C> }${ _CamelizeHelp<Rest> }`
    : P

type Camelize<T> =
  T extends Record<string, unknown>
    ? { [P in keyof T as _CamelizeHelp<P>]: Camelize<T[P]> }
    : T extends [infer F, ...infer Rest]
      ? [Camelize<F>, ...Camelize<Rest>]
      : T

Solution by lvjiaxuan #20036

type CamelizeSingleWord<S extends string, Total extends string = ''> = S extends `${infer F}_${infer R}`
  ? CamelizeSingleWord<R, `${Total}${'' extends Total ? F : Capitalize<F>}`>
  : '' extends Total
  ? S
  : `${Total}${Capitalize<S>}`;

type Camelize<T> = T extends unknown[]
  ? T extends [infer F, ...infer R]
    ? [Camelize<F>, ...Camelize<R>]
    : []
  : { [p in keyof T as p extends string ? CamelizeSingleWord<p> : never]: T[p] extends object ? Camelize<T[p]> : T[p] };

Solution by CaoXueLiang #19261

// your answers

type CamelizeKey<K> = K extends `${infer F}_${infer R}`
  ? `${F}${CamelizeKey<Capitalize<R>>}`
  : K

type Camelize<T> = T extends unknown[]
  ? T extends [infer F, ...infer R]
    ? [Camelize<F>, ...Camelize<R>]
    : []
  : {
    [P in keyof T as CamelizeKey<P>]: T[P] extends object
      ? Camelize<T[P]>
      : T[P]
  }

Solution by humandetail #16482

// your answers
type Cm<
    S extends string> =
    S extends `${infer R extends string}_${infer U extends string}`
    ?`${Capitalize<Lowercase<R>>}${Cm<U>}`
    : Capitalize<Lowercase<S>>  ;

type CamelCase<
    S> =
    S extends string
    ? Uncapitalize<Cm<S>>
    : S ;

type Camelize<T> =
    T extends readonly any[]
    ? T extends [infer R,...infer U]
    ? [Camelize<R>,...Camelize<U>]
    : []
    : T extends object
    ? {[J in keyof T as CamelCase<J>] :  Camelize<T[J]>} 
    : T ;

Solution by justBadProgrammer #15909