You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React/Redux/TS应用中如何正确为Fetch相关函数及Promise进行类型定义?

Fixing Promise & TypeScript Type Annotations for Your React/Redux App

Hey there! That TypeScript error you're hitting makes total sense—right now, TypeScript can't confirm that your storyList is actually an array of IStory objects, so it's defaulting to unknown[]. Let's walk through fixing each function's type annotations step by step to clear this up.

Root Cause

The main issues are:

  • Missing explicit return types on async functions, so TypeScript can't infer types correctly
  • checkResponse returns an untyped value, leading to ambiguity
  • Error handling in some functions leaves room for unexpected undefined values in your story array

Step 1: Make checkResponse Generic

First, let's turn checkResponse into a generic function so it can return the correct type based on what we expect from the API:

const checkResponse = <T>(response: Response): Promise<T> => {
  if (response.ok) {
    return response.json() as Promise<T>;
  }
  return Promise.reject(new Error("Frontend error")); // Use Error objects instead of strings for better error handling
};

This way, when we call checkResponse, we can explicitly tell TypeScript what type we expect the JSON to be.

Step 2: Fix fetchStoryIdList Type

Replace Promise<any> with the actual type we expect (number[]), and make sure we don't silently swallow errors (right now, your catch block logs but doesn't rethrow, which means the function could return undefined instead of an array):

const fetchStoryIdList = async (): Promise<number[]> => {
  try {
    const response = await fetch(`${BASE_URL}/newstories.json`);
    const idJson = await checkResponse<number[]>(response);
    return idJson;
  } catch (error) {
    console.error(error);
    throw error; // Re-throw so upstream functions can handle the error
  }
};

Step 3: Fix fetchStoryList Type & Filter Invalid Entries

Add an explicit return type (Promise<IStory[]>), and filter out any undefined values that might come from failed API calls (since your catch block in the map returns nothing, which is undefined):

const fetchStoryList = async (): Promise<IStory[]> => {
  const idList = await fetchStoryIdList();
  const promises = idList.slice(0, 100).map(async (id: number): Promise<IStory | undefined> => {
    try {
      const response = await fetch(`${BASE_URL}/item/${id}.json`);
      return checkResponse<IStory>(response);
    } catch (err) {
      console.error(`Failed to fetch story ${id}:`, err);
      return undefined; // Explicitly return undefined on failure
    }
  });

  const storyList = await Promise.all(promises);
  // Filter out any undefined entries to ensure we only have valid IStory objects
  return storyList.filter((story): story is IStory => story !== undefined);
};

The story is IStory type predicate tells TypeScript that after filtering, the array is definitely IStory[].

Step 4: Update fetchStories to Match

Now TypeScript will correctly infer that storyList is IStory[], so the dispatch error will disappear. We can also add a bit more type safety to the thunk:

const fetchStories = () => {
  return async (dispatch: Dispatch<StoryActions>) => {
    try {
      dispatch(fetchStoryAction());
      const storyList = await fetchStoryList();
      // No more error here! TypeScript knows storyList is IStory[]
      dispatch(fetchStorySuccessAction(storyList));
    } catch (error) {
      console.error("Failed to fetch stories:", error);
      // Optional: Dispatch an error action here too
      // dispatch(fetchStoryErrorAction(error instanceof Error ? error.message : "Unknown error"));
    }
  };
};

Full Corrected Code Snippet

Putting it all together, here's the updated code with proper type annotations:

export interface IStory {
  by: string;
  descendants: number;
  id: number;
  kids: number[];
  score: number;
  time: number;
  title: string;
  type: string;
  url: string;
}

export interface IStoryState {
  storyList: IStory[];
  storyError: null | string;
  isStoryLoading: boolean;
}

// Assume StoryActionTypes and StoryActions are defined correctly
export const fetchStorySuccessAction = (storyList: IStory[]): IFetchStoryDataSuccessAction => {
  return {
    type: StoryActionTypes.FETCH_STORY_DATA_SUCCESS,
    payload: storyList,
  };
};

const checkResponse = <T>(response: Response): Promise<T> => {
  if (response.ok) {
    return response.json() as Promise<T>;
  }
  return Promise.reject(new Error("Frontend error"));
};

const fetchStoryIdList = async (): Promise<number[]> => {
  try {
    const response = await fetch(`${BASE_URL}/newstories.json`);
    const idJson = await checkResponse<number[]>(response);
    return idJson;
  } catch (error) {
    console.error(error);
    throw error;
  }
};

const fetchStoryList = async (): Promise<IStory[]> => {
  const idList = await fetchStoryIdList();
  const promises = idList.slice(0, 100).map(async (id: number): Promise<IStory | undefined> => {
    try {
      const response = await fetch(`${BASE_URL}/item/${id}.json`);
      return checkResponse<IStory>(response);
    } catch (err) {
      console.error(`Failed to fetch story ${id}:`, err);
      return undefined;
    }
  });

  const storyList = await Promise.all(promises);
  return storyList.filter((story): story is IStory => story !== undefined);
};

const fetchStories = () => {
  return async (dispatch: Dispatch<StoryActions>) => {
    try {
      dispatch(fetchStoryAction());
      const storyList = await fetchStoryList();
      dispatch(fetchStorySuccessAction(storyList));
    } catch (error) {
      console.error("Failed to fetch stories:", error);
    }
  };
};

Key Takeaways

  • Use generic functions for API response parsing to keep types consistent
  • Always add explicit return types to async functions (avoid any like the plague!)
  • Handle edge cases like failed API calls by filtering out invalid entries
  • Re-throw errors in lower-level functions so upstream code can handle them properly

内容的提问来源于stack exchange,提问作者qweezz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 08:24:07