Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript how to map type keys to camelCase

Tags:

typescript

I'm new to typescript but I want to create a mapped type that converts keys from another type. Specifically, say I have a type where all the keys are snake-cased, how can I create a type where they are all camel-cased?

I thought I could do something like

type CamelCase<T> = {
  [_.camelCase(P in keyof T)]: T[P];
}

type MyCamelCaseType = CamelCase<SnakeCaseType>;

But TS doesn't like this. How can I transform the keys of an existing type to create a new type in this way?

like image 701
amdilley Avatar asked Jan 07 '20 06:01

amdilley


2 Answers

In Typescript 4.1, template literal types got quite an upgrade. This is a problem I have wanting to be solved for a while and with a bit of tweaking managed to come up with this:

type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
  ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
  : Lowercase<S>

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


interface SnakeCase {
    bar_value: string;
    baz_value: {
        blah_test: number;
    }
}

const transformed: KeysToCamelCase<SnakeCase> = {
    bazValue: {
        blahTest: 2
    },
    barValue: 'test'
}

I can recommend reading: https://dev.to/phenomnominal/i-need-to-learn-about-typescript-template-literal-types-51po and also trying some of these Typescript challenges https://github.com/type-challenges/type-challenges to learn more about these literal types.

like image 121
Max Eisenhardt Avatar answered Sep 19 '22 14:09

Max Eisenhardt


In case you have an object with nested arrays in it, you can wrap it a bit more. Based on Max Eisenhardts answer.

We can check on an Array type, and if so, take the object type of that Array in the already existing functionality.

If we don't do this, array functions will be marked as not callable.

 type CamelCase<S extends string> = S extends `${infer P1}_${infer P2}${infer P3}`
    ? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
    : Lowercase<S>

  type ObjectToCamel<T> = {
    [K in keyof T as CamelCase<string &K>]: T[K] extends Record<string, any> ? KeysToCamelCase<T[K]> : T[K]
  }

  type KeysToCamelCase<T> = {
    [K in keyof T as CamelCase<string &K>]: T[K] extends Array<any> ? KeysToCamelCase<T[K][number]>[] : ObjectToCamel<T[K]>
  }  
like image 22
Hespen Avatar answered Sep 20 '22 14:09

Hespen