Search code examples
reactjstypescriptreact-typescript

Property 'Error' does not exist on type 'MovieData | ResponseError'. Property 'Error' does not exist on type 'MovieData'


Answered

So I have following interfaces: MovieData

export interface MovieData {
  Poster: string;
  Title: string;
  Plot: string;
  imdbID: string;
}

and interface ResponseError

export interface ResponseError {
  Response: 'False',
  Error: string,
}

I also have function getMovie() thet returns Promise, resolve type is MovieData , and reject type is ResponseError

import { MovieData } from './types/MovieData';
import { ResponseError } from './types/ReponseError';

const API_URL = 'https://www.omdbapi.com/?apikey=**********';

export function getMovie(query: string): Promise<MovieData | ResponseError> {
  return fetch(`${API_URL}&t=${query}`)
    .then(res => res.json())
    .catch(() => ({
      Response: 'False',
      Error: 'unexpected error',
    }));
}

In FindMovie function component I invoke handleSubmit function, and pass search string inside getMovie function. In then() method of promise I want to check either promise was rejected , or fullfiled. In case rejected - I check if res object has property Error. type of res - if you hover mouse over res property in then() - res: MovieData | ResponseError

import { useState } from 'react';
import { getMovie } from '../../api';

export const FindMovie: React.FC = () => {
  const [search, setSearch] = useState<string>('');
  const [error, setError] = useState<boolean>(false);

  const handleSubmit = () => {
    if (search === '') {
      return;
    }

    getMovie(search)
      .then(res => {
        if (res?.Error) {
          console.log(res?.Error)
        }
      });

    setError(false);
  };

QUESTION!!!!!!!!

in terminal I recieve error: Property 'Error' does not exist on type 'MovieData | ResponseError'. Property 'Error' does not exist on type 'MovieData'.

I tried to use import separetly interface ResponseError to file with findMovie function component

import { ResponseError } from '../../types/ReponseError';

and to perform next check

.then(res => {
   if (res instanceof ResponseError) {
       console.log(res?.Error)
   }
});

But I recieve even wierder error message: Attempted import error: 'ResponseError' is not exported from '../../types/ReponseError'.

Will be glad for every advise.


Solution

  • You need a type guard here to check if your object is of type ResponseError.

    You cannot use instanceof here, since that is used to check if an object belongs to a certain class.

    Whereas in your case, you are using interface and type.

    Defining a type guard like:

    function isResponseError(obj: any): obj is ResponseError {
        return obj.Error !== undefined;
    }
    

    and then doing:

    .then(res => {
       if (isResponseError(res)) {
           console.log(res.Error)
       }
    });
    

    should solve your problem.

    You can check out this stackoverflow answer for more info on type guards vs instanceof: How to check the object type on runtime in TypeScript?

    EDIT: Replying to your answer below:

    That's also a decent way to go about it!

    Allow me to recommend another approach. Your MovieData type could be:

    export interface MovieData {
      data?: {
        Poster: string;
        Title: string;
        Plot: string;
        imdbID: string;
      }
      error?: {
        Response: 'False',
        Error: string,
      }
    }
    

    Then in your getMovies function:

    1. If there is no error > you could set set the data field to the appropriate values .
    2. Else > you could set the error field to the error coming from the API

    And in your frontend you can check for something like:

    if (res.error !== undefined) {
      setError(true);
    }
    

    This might be a cleaner approach since

    • Its more obvious that you are checking for an error if you do res.error === something rather than res.title === something
    • I've seen several APIs (eg. Apollo client, Stripe APIs) handle sending errors and data from an API call in this way, which is why I recommend this as best practice.