#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. Спасибо за очень подробное объяснение!