Axios post request with typescript issue

Viewed 10677

I am trying to add typescript to my current project, so after using it with Axios post request i am facing an issue.

Usecase is that i want to send "email" "firstname" "lastname" and "password" in my post request and in response i want "accessToken"

Here is the code below

export interface UserRegistrationModel {
  email: string;
  password: string;
  firstname: string;
  lastname: string;
}



export const register = async (user: UserRegistrationModel) => {
  const { data } = await http.post<UserRegistrationModel>("/users", user);
  return data;
};

This is my function called after i submit the registration form like

register(values)
        .then((data) => {
          console.log(data.accessToken);
          setLoading(false);

        })
        .catch(() => {
          setLoading(false);

        });

so here in my then block when i try to access "accessToken" by data.acessToken is throws error

Property 'accessToken' does not exist on type 'UserRegistrationModel

so i tried to define a new Interface named "AuthModel" but then when i assign it to data like

export interface AuthModel {
  accessToken: string
}


 register(values)
        .then((data:AuthModel) => {
          console.log(data.accessToken);
          setLoading(false);
        })
        .catch(() => {
          setLoading(false);
          setSubmitting(false);
          setStatus("Registration process has broken");
        });

it says

Argument of type '(data: AuthModel) => void' is not assignable to parameter of type '(value: UserRegistrationModel) => void | PromiseLike'. Types of parameters 'data' and 'value' are incompatible. Property 'accessToken' is missing in type 'UserRegistrationModel' but required in type 'AuthModel'.

Below is the axios config in anyone wants to see

// http.ts

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

enum StatusCode {
  Unauthorized = 401,
  Forbidden = 403,
  TooManyRequests = 429,
  InternalServerError = 500,
}

const headers: Readonly<Record<string, string | boolean>> = {
  Accept: "application/json",
  "Content-Type": "application/json; charset=utf-8",
  "Access-Control-Allow-Credentials": true,
  "X-Requested-With": "XMLHttpRequest",
};

// We can use the following function to inject the JWT token through an interceptor
// We get the `accessToken` from the localStorage that we set when we authenticate
const injectToken = (config: AxiosRequestConfig): AxiosRequestConfig => {
  try {
    const token = localStorage.getItem("accessToken");

    if (token != null) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  } catch (error: any) {
    throw new Error(error);
  }
};

class Http {
  private instance: AxiosInstance | null = null;

  private get http(): AxiosInstance {
    return this.instance != null ? this.instance : this.initHttp();
  }

  initHttp() {
    const http = axios.create({
      baseURL: "https://api.example.com",
      headers,
      withCredentials: true,
    });

    http.interceptors.request.use(injectToken, (error) =>
      Promise.reject(error)
    );

    http.interceptors.response.use(
      (response) => response,
      (error) => {
        const { response } = error;
        return this.handleError(response);
      }
    );

    this.instance = http;
    return http;
  }

  request<T = any, R = AxiosResponse<T>>(
    config: AxiosRequestConfig
  ): Promise<R> {
    return this.http.request(config);
  }

  get<T = any, R = AxiosResponse<T>>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.get<T, R>(url, config);
  }

  post<T = any, R = AxiosResponse<T>>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.post<T, R>(url, data, config);
  }

  put<T = any, R = AxiosResponse<T>>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.put<T, R>(url, data, config);
  }

  delete<T = any, R = AxiosResponse<T>>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<R> {
    return this.http.delete<T, R>(url, config);
  }

  // Handle global app errors
  // We can handle generic app errors depending on the status code
  private handleError(error: any) {
    const { status } = error;

    switch (status) {
      case StatusCode.InternalServerError: {
        // Handle InternalServerError
        break;
      }
      case StatusCode.Forbidden: {
        // Handle Forbidden
        break;
      }
      case StatusCode.Unauthorized: {
        // Handle Unauthorized
        break;
      }
      case StatusCode.TooManyRequests: {
        // Handle TooManyRequests
        break;
      }
    }

    return Promise.reject(error);
  }
}

export const http = new Http();
3 Answers

There is an ability to set different types to input an output data (axios definitions):

post<T = never, R = AxiosResponse<T>>(url: string, data?: T, config?: AxiosRequestConfig<T>): Promise<R>;

So, in your case it would be something like:

export interface UserRegistrationModel {
  email: string;
  password: string;
  firstname: string;
  lastname: string;
}

export const register = async (user: UserRegistrationModel) => {
  const { data } = await http.post<UserRegistrationModel, AxiosResponse<{ accessToken: string }>>("/users", user);
  return data;
};

Change your register function to:

export const register = async (user: UserRegistrationModel) => {
  const { data } = await http.post<AuthModel>("/users", user);
  return data;
};

Also inside your class, the type for post should be:

post<T = any>(
    url: string,
    data?: T,
    config?: AxiosRequestConfig
  ): Promise<T> {
    return this.http.post(url, data, config);
}

Property 'accessToken' does not exist on type 'UserRegistrationModel

You set the return type for data to be UserRegistrationModel, and typescript is telling you that accessToken does not exist on that type. This should be obvious from your definition.

Argument of type '(data: AuthModel) => void' is not assignable to parameter of type '(value: UserRegistrationModel) => void | PromiseLike'. Types of parameters 'data' and 'value' are incompatible. Property 'accessToken' is missing in type 'UserRegistrationModel' but required in type 'AuthModel'.

You are trying to pass a function which expects AuthModel as the only argument, but once again your register function will pass UserRegistrationModel to that function. Typescript is just informing you of this.

This tricky type assertion works for me, component code:

let response = (await UserService.LoginUser(
  email,
  password
)) as unknown as AxiosResponse<JwtResponse>;

Post method: Where T is generic type and AxiosResponse is overriden to my interfaces (just rewrite, no changes yet)

post: async function <T>(URL: string, data: any) {
return await client.post<T, AxiosResponse<T>>(URL, data);
},

Then, in services I basically return resolved promise like this:

return await API.post<JwtResponse>(`api/User/login`, {
  Email: email,
  Password: passowrd,
})
  .then((result: AxiosResponse<JwtResponse>) => {
    CookieService.SetCookie("jwt", result.data);
    return result;
})
Related