Search code examples
javascripttypescriptjavascript-objectstransformationclass-transformer

How to transform response from API request into class instance with `class-transformer` in TypeScript?


I have normal method Request(method: HttpMethod, url: string, ...) for calling API. I am using TypeScript.

I need to transform response from this API Request into class instance with class-transformer (or without it :D).

Example:

class UserResponse {
  id: number;l
  foo: string;
  bar: string;
}
const user = await Request(HttpMEthod.GET, '/user/1');
// `user` should be class instance `UserResponse`

I know I cannot use generics like so:

const Request = <T>(method: HttpMethod, url: string, ...) => {
  // axios or something else...
  return plainToClass(T, res);
}

const user = await Request<UserResponse>(HttpMEthod.GET, '/user/1');

Generics do not work that way, but I can do something like:

const Request = <T>(method: HttpMethod, url: string, ...,  transformTo?: { new (): T }) => {
  // axios or something else...
  return plainToClass(transformTo, res);
}

const user = await Request(HttpMEthod.GET, '/user/1', ... , new UserResponse());

But this also not working. I am still getting user type:

const user: unknown

What I am doing wrong?


Solution

  • First and foremost, what is Request? I am assuming this returns a Promise.

    You have a couple of issues:

    1. You never actually map your response into a new UserResponse
    2. Generics in TypeScript will not new up a response. It acts more like an interface where it's more a data contract rather than a full initialized class.

    Here is how you need to do this:

    class UserResponse {
      public id: number;
      public foo: string;
      public bar: string;
      constructor(user: UserResponse) {
        Object.assign(this, user); // or set each prop individually
      }
    }
    const response = await Request(HttpMEthod.GET, '/user/1');
    const user = new UserResponse(response);

    Basically, you need to add a constructor to your class (otherwise, just use an interface). Once you've got the constructor, then you can new up a new UserResponse class from the response.

    Also, if Request looks like:

    const Request = <T>(method: HttpMethod, url: string, ...,  transformTo?: { new (): T }) => {
      // axios or something else...
      return plainToClass(transformTo, res);
    }
    

    I think you want to do this instead: const user = await Request(HttpMEthod.GET, '/user/1', ... , UserResponse);