skip to Main Content
import { useCallback, useReducer } from "react";

const ARTIST_REDUCER_TYPES = {
  ADD: "ADD",
};

const artistArray = [...Object.values(ARTIST_REDUCER_TYPES)] as const;

type ActionReturnedTypes = (typeof artistArray)[number];

type ReducerStateType = {
  artist: { name: string };
  albums: { name: string, released: number }[];
  topTracks: { name: string released: number }[];
};

type ReducerAction = {
  type: ActionReturnedTypes;
  payload: ReducerStateType;
};

const artistInitState = {
  artist: { name: ""},
  albums: [{name: "", released: 0}],
  topTracks: [{name: "", released: 0}],
};

export const useArtistResults = (initialState: typeof artistInitState) => {
  const [artistDetails, dispatch] = useReducer(
    (state: ReducerStateType, action: ReducerAction): ReducerStateType => {
      switch (action.type) {
        case ARTIST_REDUCER_TYPES.ADD: {
          if (!action.payload) {
            throw new Error("ADD action requires a payload");
          }

          return {
            ...state,
            artist: action.payload.artist,
            albums: action.payload.albums,
            topTracks: action.payload.topTracks,
          };
        }
      }
    },
    artistInitState
  );

  const setProfile = useCallback(
    (artist, albums, topTracks) => {
      dispatch({
        type: "ADD",
        payload: {
          artist,
          albums,
          topTracks,
        },
      });
    },
    []
  );

This is constantly returning this error:

No overload matches this call.

If I take off the type for the state parameter the error goes away. I just don’t seem to know what is causing this. Does this have something to do with the state object not matching the initial state?

I’m so confused

2

Answers


  1. Chosen as BEST ANSWER

    This was an issue with the actual type for the state being slightly incorrect with the initial state.

    My above code was an attempt at making it as reproducible as possible. In reality, my code and state is a lot larger than the example I put above so it wasn't a good example

    All I had to do was fix the initialState

    export const artistInitState = {
      artist: {
        external_urls: { spotify: "" },
        followers: { href: null, total: 0 },
        genres: [],
        href: "",
        id: "",
        images: [{ height: 0, url: "", width: 0 }],
        name: "",
        popularity: 0,
        type: "",
        uri: "",
      },
      albums: [
        {
          album_group: "",
          album_type: "",
          artists: [],
          available_markets: [],
          external_urls: { spotify: "" },
          href: "",
          id: "",
          images: [{ height: 0, url: "", width: 0 }],
          name: "",
          release_date: "",
          release_date_precision: "",
          total_tracks: 0,
          type: "",
          uri: "",
        },
      ],
      topTracks: [
        {
          album: {
            album_group: "",
            album_type: "",
            href: "",
          },
          artists: [],
          disc_number: 0,
          duration_ms: 0,
          explicit: false,
          external_ids: { isrc: "" },
          external_urls: {
            spotify: "",
          },
          href: "",
          id: "",
          is_local: false,
          is_playable: true,
          name: "",
          popularity: 0,
          preview_url: "",
          track_number: 0,
          type: "",
          uri: "",
        },
      ],
    };
    
    

    There were a few mistakes in there so it didn't match the ReducerStateType

    type ReducerStateType<T> = {
      artist: ArtistDetailsType;
      albums: AlbumDetailsType[] | T;
      topTracks: TopTracksDetailsType[] | T;
    };
    
    export const useArtistResults = (initialState: typeof artistInitState) => {
      const [artistDetails, dispatch] = useReducer(
        (
          state: ReducerStateType<NoAlbumOrTracksType>,
          action: ReducerAction
        ): ReducerStateType<NoAlbumOrTracksType> => {
          switch (action.type) {
            case ARTIST_REDUCER_TYPES.ADD: {
              if (!action.payload) {
                throw new Error("ADD action requires a payload");
              }
    
              let albums: AlbumDetailsType[] | { noAlbums: "no albums" } = action
                .payload.albums.length
                ? [
                    ...new Map(
                      action.payload.albums.map((item) => [item.name, item])
                    ).values(),
                  ]
                : { noAlbums: "no albums" };
    
              let topTracks: TopTracksDetailsType[] | { noTracks: "no tracks" } =
                action.payload.topTracks.length
                  ? action.payload.topTracks
                  : { noTracks: "no tracks" };
    
              return {
                ...state,
                artist: action.payload.artist,
                albums,
                topTracks,
              };
            }
            default:
              return { ...state };
          }
        },
        initialState
      );
    
      const setProfile = useCallback<ArtistAndAlbumStateSetter>(
        (artist, albums, topTracks) => {
          dispatch({
            type: "ADD",
            payload: {
              artist,
              albums,
              topTracks,
            },
          });
        },
        []
      );
    
      const { artist, albums, topTracks } = artistDetails;
    
      return { artist, albums, topTracks, setProfile };
    };
    
    

  2. Try removing the explicit ReducerStateType return type from the reducer function and adding a const assertion to ARTIST_REDUCER_TYPES:

    const ARTIST_REDUCER_TYPES = {
      ADD: "ADD",
    } as const;
    

    This changes the type of that value from:

    {
       ADD: string
    }
    

    to:

    {
      readonly ADD: "ADD"
    }
    

    This allows TypeScript to know that the switch inside your reducer is exhaustive.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search