Аргумент типа «Уведомления» не может быть присвоен параметру типа «Уведомления [] | Запись»

#reactjs #typescript #redux #redux-toolkit

#reactjs #typescript #redux #redux-toolkit

Вопрос:

Я сталкиваюсь с этой проблемой при создании асинхронного блока с использованием Redux Toolkit и использовании указанного блока в качестве дополнительного средства.

Когда я указываю Return argument , Thunk Argument и тому ThunkApiConfig подобное, он выдает ошибку из заголовка вопроса в строке notificationsAdapter.upsertMany() :

 export const fetchNotifications = createAsyncThunk<
  Notifications,
  void,
  { state: RootState }
>("notifications/fetchNotifications", async (_, { getState }) => {
  const allNotifications = selectAllNotifications(getState());
  const [latestNotification] = allNotifications;
  const latestTimestamp = latestNotification ? latestNotification.date : "";
  const response = await client.get(
    `/fakeApi/notifications?since=${latestTimestamp}`
  );
  return response.notifications;
});

const notificationsSlice = createSlice({
  name: "notifications",
  initialState,
  reducers: {
    allNotificationsRead(state) {
      Object.values(state.entities).forEach((notification) => {
        notification amp;amp; (notification.read = true);
      });
    },
  },
  extraReducers: (builder) => {
    builder.addCase(fetchNotifications.pending, (state) => {
      state.status = "loading";
    });
    builder.addCase(fetchNotifications.rejected, (state, action) => {
      state.status = "failed";
      state.error = action.error.message as Error;
    });
    builder.addCase(fetchNotifications.fulfilled, (state, action) => {
      state.status = "succeeded";
      Object.values(state.entities).forEach((notification) => {
        notification amp;amp; (notification.isNew = !notification.read);
      });
      notificationsAdapter.upsertMany(state, action.payload);
    });
  },
});
 

Но когда я удаляю типы из createAsyncThunk и утверждаю, что getState() это RootState (который поступает из хранилища с использованием export type RootState = ReturnType<typeof store.getState> , ошибки больше нет, поэтому я не уверен, что не так со значениями, которые я ранее установил.

 export const fetchNotifications = createAsyncThunk(
  "notifications/fetchNotifications",
  async (_, { getState }) => {
    const allNotifications = selectAllNotifications(getState() as RootState);
    const [latestNotification] = allNotifications;
    const latestTimestamp = latestNotification ? latestNotification.date : "";
    const response = await client.get(
      `/fakeApi/notifications?since=${latestTimestamp}`
    );
    return response.notifications;
  }
);
 

Код для этого можно найти здесь .

Комментарии:

1. Я думаю, что ваш возвращаемый тип должен быть Notifications[] таким, как если бы вы возвращали массив объектов, а не только одну запись (и, возможно Notification , было бы лучшим именем для интерфейса, описывающего один элемент!)

Ответ №1:

Вы должны быть очень осторожны с as утверждениями, потому что вы создадите проблемы, если будете утверждать что-то, что окажется неправильным. Например, ваш тип Error есть string | null , но action.error.message есть string | undefined . Что происходит, когда это undefined ?

Вместо того, чтобы настаивать на Typescript, что у вас правильный тип:

 state.error = action.error.message as Error;
 

На самом деле вы должны обеспечить, чтобы у вас был правильный тип, используя нулевое объединение для замены undefined на null :

 state.error = action.error.message ?? null;
 

комментарий @Nadia верен. upsertMany ожидает либо массив Notifications[] , либо объект с ключом Record<EntityId, Notifications> . Ваше fetchNotifications действие возвращает одно уведомление Notifications . Ваш client.get ответ таков any , что вы не получаете никаких ошибок из-за возврата неправильного типа.

Когда вы удаляете типы, вы не получаете никаких ошибок, потому что теперь ваше fetchNotifications действие возвращается any .

Вы хотите убедиться, что возвращаете массив Notifications[] .


На мой взгляд, лучший способ избежать подобных ошибок — иметь строго типизированный client файл, который может возвращать правильный тип на основе конечной точки.

 interface EndpointMap {
  "/fakeApi/notifications": Notifications;
}

interface Client {
  getOne<K extends keyof EndpointMap>(
    endpoint: K,
    id: string
  ): Promise<EndpointMap[K]>;

  getMany<K extends keyof EndpointMap>(
    endpoint: K,
    args: Record<string, any>
  ): Promise<EndpointMap[K][]>;
}
 

Комментарии:

1. Спасибо за очень подробное объяснение!