Home > Net >  How to use discriminated union properly for function return
How to use discriminated union properly for function return

Time:12-23

I'm new to using typescript. I was wondering how to utilize discriminated union for function return. I have an async function that calls 2 endpoints, and I want to correctly type the return value based on each api call results.

So far I managed to created these types:

interface GetDetailSuccess {
  status: "success";
  data: MovieDetailResult;
}

interface GetDetailFail {
  status: "failed";
  error: any;
}

interface GetCastSuccess {
  status: "success";
  data: MovieCastResult
}

interface GetCastFail {
  status: "failed";
  error: any;
}

type MovieDetail = GetDetailSuccess | GetDetailFail;
type MovieCast = GetCastSuccess | GetCastFail;

type ReturnValue = {
  movieDetail: MovieDetail;
  movieCast: MovieCast;
};

Here is the simplified version of the function that I managed to create so far:

export const getMovieDetailAndCast = async ():Promise<ReturnValue> => {

    const movDet = {} as MovieDetail;
    const movCas = {} as MovieCast;
  
    await Promise.allSettled([
      api.getMovieDetail(),
      api.getMovieCast(),
    ])
      .then((responses) => {
        responses.forEach((res, index) => {
          if (index === 0) {
            if (res.status === "fulfilled") {
              movDet.status = "success";
              if (movDet.status === "success") {
                movDet.data = res.value.data;
              }
            }
  
            if (res.status === "rejected") {
              movDet.status = "failed";
              if (movDet.status === "failed") {
                movDet.error = res.reason.response.data.status_message;
              }
            }
          }
  
          if (index === 1) {
            if (res.status === "fulfilled") {
              movCas.status = "success";
              if (movCas.status === "success") {
                movCas.data = res.value.data;
              }
            }
  
            if (res.status === "rejected") {
              movCas.status = "failed";
              if (movCas.status === "failed") {
                movCas.error = res.reason.response.data.status_message;
              }
            }
          }
        });
      })
      .catch((err) => {
        console.error(err);
      });
  
    return {
        movieDetail: movDet,
        movieCast: movCas,
    };
}

So far the IDE doesn't yell at me for any error, but I do wonder if what I am doing is correct. Especially the part on how to narrowing the type and the part where I assigned an empty objects using as. Is there anything that I could to improve the coding above? Any feedback would be appreciated

CodePudding user response:

It's not clear what you want to achieve using different types for success and failure. Usually, the easiest way to get if it is a success or failure is checking if the error property is defined or if the status property is "success". I think you should narrow your types like so to reduce complexity:

export const enum GetStatus {
  PENDING = "pending",
  SUCCESS = "success",
  FAILED = "failed"
}

export interface GetDetail {
  status: GetStatus;
  data: MovieDetailResult | undefined;
  error: any;
}

export interface GetCast {
  status: GetStatus;
  data: MovieCastResult | undefined;
  error: any;
}

export interface DetailAndCast {
  movieDetail: GetDetail;
  movieCast: GetCast;
};

Then, if you follow this suggestion you might want to keep track of your call with a "pending" status.

export const getMovieDetailAndCast: () => Promise<DetailAndCast> = async () => {

  const movieDetail: GetDetail = {status: GetStatus.PENDING, data: undefined, error: undefined};
  const movieCast: GetCast = {status: GetStatus.PENDING, data: undefined, error: undefined};
  
  await Promise.all([
    api.getMovieDetail(),
    api.getMovieCast(),
  ])
    .then(responses => {
      responses.foreach((res, index) => {
        const returnValue = index === 0 ? movieDetail : movieCast;
        if (res.status === "fulfilled") {
          returnValue.status = GetStatus.SUCCESS;
          returnValue.data = res.value.data;
        } else if (res.status === "rejected") {
          returnValue.status = GetStatus.FAILED;
          returnValue.error = res.reason.response.data.status_message;
        }
      });
    })
    .catch(err => console.error(err));
  
  return {movieDetail, movieCast};
}
  • Related