Typescript won't allow access to data property on endpoint response

53 Views Asked by At

I'm having problems trying to access an object within my endpoint response because of TS. Basically the problem is that I need to evaluate response.data.data even if response.data.success is false, and at that point TS won't let me access the data object inside my endpoint response. Here are my important types and interfaces:

type ApiResponse<D = any, M = any> = ApiResponseSuccess<D, M> | ApiResponseError;

interface ApiResponseSuccess<D, M = any> {
    data: D;
    meta?: M;
    success: true;
    message?: string;
    valid?: boolean;
}

interface ApiResponseError {
    success: false;
    message: string;
    error_code: string;
    valid: boolean;
}

interface MessageData {
    total_messaged: number;
    message:        string;
}

interface MessageErrors {
    beauty_ids: string[];
}

interface MessageModels {
    success: string;
    data:    MessageData;
    message: string;
    errors:  MessageErrors;
}

This is the function where I'm sending the request to my endpoint:

    const sendMessageToAllCastingApplicants = (finalObject: object) => {
        sendMessageToAllApplicants({ context }, finalObject)
            .then((response) => {
                const responseData = response.data;
                if (responseData.success) {
                    const { message } = responseData.data.data; // Destructure data properties
                    openToast(i18n.t(message), 'is-success'); // Use message directly
                }
                if (!responseData.success || responseData.data.data.total_messaged === 0) {
                    openToast(i18n.t(responseData.data.data.message), 'is-danger'); // Use message directly
                }
            })
            .catch((error) => {
                // Handle the error appropriately
                openToast(i18n.t(error.message), 'is-danger');
            });
    };

This is the sendMessageToAllApplicants where I'm sending a post request via axios and typing the response:

export const sendMessageToAllApplicants = makeCall((axios, finalObject) => {
    return axios.post<ApiResponse<MessageModels>>(`/messages-models`, finalObject);
});

As you can see I need to evaluate !responseData.success and at this point TS is not allowing the access to responseData.data.data.message (using the VSC I can see that it only allow access to the top level properties).

So in this block of code:

 if (!responseData.success || responseData.data.data.total_messaged === 0) {
    openToast(i18n.t(responseData.data.data.message), 'is-danger'); // Use message directly
}

I'm getting:

Property 'data' does not exist on type 'ApiResponse<MessageModels, any>'. Property 'data' does not exist on type 'ApiResponseError'.

when trying to access responseData.data

This is the important part of my API response:

{
  "data": {
    "success": "true",
    "data": {
      "total_messaged": 1,
      "message": "Messages sent successfully"
    }
  },
}

On top of that I really don't understand why do I have to do something like responseData.data.data.message if I'm already declaring responseData = response.data. Any help will be really appreacited.

1

There are 1 best solutions below

2
DustInComp On
Property 'data' does not exist on type 'ApiResponse<MessageModels, any>'.
  Property 'data' does not exist on type 'ApiResponseError'.

This will appear because you're trying to access data before narrowing down the type to an ApiResponseSuccess<D, M> by handling the success == false case.

TS Playground

(response) => {
  // response may be an ApiResponseError at this point
  if (!response.success) {
    throw new Error('Request failed')
  }
  // response is now an ApiResponseSuccess<MessageModels, any> and has data
  const responseData = response.data;
  if (responseData.success) {
    const { message } = responseData.data; // Destructure data properties
    openToast(i18n.t(message), 'is-success'); // Use message directly
  }
  if (!responseData.success || responseData.data.total_messaged === 0) {
    openToast(i18n.t(responseData.data.message), 'is-danger'); // Use message directly
  }
}