Search code examples
reactjstypescriptreduxmiddleware

How to create dynamic typescript type: The expected type comes from property 'type' which is declared here on type 'AppActions'


I'm using Typescript and react-redux, and I trying to make custom dynamic middleware for my API request.

Here is my code:

import { Dispatch } from "react";
import { errorHandler } from "../components/Layout/SnackBar/alert";
import { API } from "../_helpers/api";
import { getTheTime } from "../_helpers/constants";
import { AppActions, AppState } from "../_types";

export const BankPages = {
  posts: { name: "POSTS", api: "/post/v2" },
  lessons: { name: "LESSONS", api: "/lesson/v2" },
  guides: { name: "GUIDES", api: "/lesson/v2/guide" },
  courses: { name: "COURSES", api: "/lesson/v2/course" },
  exercises: { name: "EXERCISES", api: "/lesson/v2?course=125" },
};

export const requestBank = (page: keyof typeof BankPages) => (
  dispatch: Dispatch<AppActions>,
  getState: () => AppState
) => {
  const bankState = getState().bank[page];
  const currentTime = getTheTime();
  const resetTime = currentTime - bankState.nextLoad;
  if (bankState.data && resetTime <= 0) {
    return;
  }
  dispatch({ type: `REQUEST_${BankPages[page].name}` });

  API.get(BankPages[page].api)
    .then((res) =>
      dispatch({
        type: `SUCCESS_${BankPages[page].name}`,
        payload: {
          data: res.data,
          nextLoad: getTheTime(10),
        },
      })
    )
    .catch((err) => errorHandler(err, `FAILURE_${BankPages[page].name}`));
};

I get an error on type dispatch({ type: ... }) : The expected type comes from property 'type' which is declared here on type 'AppActions'

This is what my type looks like:

export const REQUEST_POSTS = "REQUEST_POSTS";

export interface RequestPosts {
  type: typeof REQUEST_POSTS;
}

Everything works perfect, and I know it's because I declared typeof REQUEST_POSTS.

How can I fix this error?


Solution

  • After some search , I found a way for making dynamic string in Typescript 4.1+ using function like this:

    function makeType<NS extends string, N extends string>(namespace: NS, name: N) {
      return namespace + name as `${NS}${N}`
    }
    // should be used like this
    const sampleType = makeType('REQUEST_', 'POSTS');
    // return "REQUEST_POSTS"
    

    Then for getting string as type, and make dynamic string from object should use as const like this:

    export const BankPages = {
      posts: { name: "POSTS", api: "/post/v2" } ,
      lessons: { name: "LESSONS", api: "/lesson/v2" } ,
      guides: { name: "GUIDES", api: "/lesson/v2/guide" } ,
      courses: { name: "COURSES", api: "/lesson/v2/course" } ,
      exercises: { name: "EXERCISES", api: "/lesson/v2?course=125" },
    } as const;
    

    With this trick when you type BankPages.post.name shows you "POSTS" instead of string

    And dynamic dispatch should be like this:

    dispatch({ type: makeType("REQUEST_", BankPages[page].name) });