Is there a way to map incoming JSON object to typescript interface?
So, let me explain. I have tried using class-transformer
, but it does not solve my problem. In javascript, I would have used object-mapper
to transform from source to destination. My incoming object's keys can be in upper case, lower case, with hypen or underscore, but then they map to same interface. Example
Incoming Object
{
'FIRST NAME': 'John',
'last_name' : 'Doe',
'Email Address': 'John.Doe@example.com'
}
I want to map this to
{
'first_name': 'John',
'last_name': 'Doe',
'email_address': 'John.Doe@example.com'
}
The issue with class-transformer
is I can only use one name in Expose
that I can map to from the incoming object.
How I can solve this in typescript?
You can use the following TS features to accomplish this:
We'll split it by using a generic Split
type, and split by the
(space) character, replacing it with _
(underscore). In this case we use a recursive strategy to accomplish this.
I borrowed the Split
type from type-fest
, and inspired it my answer from their various change case types
// Taken from type-fest https://github.com/sindresorhus/type-fest/blob/main/source/split.d.ts
type Split<
S extends string,
Delimiter extends string,
> = S extends `${infer Head}${Delimiter}${infer Tail}`
? [Head, ...Split<Tail, Delimiter>]
: S extends Delimiter
? []
: [S];
type ConvertSpaceToUnderscore<Parts extends readonly any[], PreviousPart = never> =
Parts extends [`${infer FirstPart}`, ...infer RemainingParts]
? FirstPart extends undefined
? ''
: FirstPart extends ''
? ConvertSpaceToUnderscore<RemainingParts, PreviousPart>
: RemainingParts extends {length: 0} ? Lowercase<FirstPart> : `${Lowercase<FirstPart>}_${ConvertSpaceToUnderscore<RemainingParts, PreviousPart>}`
: '';
type Convert<T extends Record<string, string>> = {
[K in keyof T as
K extends string ? ConvertSpaceToUnderscore<Split<K, ' '>> : never
]: T[K]
}
Then use it on your type
type Input = {
'FIRST NAME': 'John',
'last_name' : 'Doe',
'Email Address': 'John.Doe@example.com'
}
type Converted = Convert<Input>
// =>
{
first_name: "John";
last_name: "Doe";
email_address: "John.Doe@example.com"
}
See it in action on TS Playground
You can add your own splitters as needed to convert any other characters.
This type then can be trivially applied to any implementation to object-to-object mapper utility as the return value.