Estaba siguiendo los tutoriales de Redux Essentials sobre cómo emplear createAsyncThunk para generar Thunks. En su ejemplo aquí , crean un thunk así:
export const addNewPost = createAsyncThunk( 'posts/addNewPost', async (initialPost) => { // Note: initialPost is an object with 3 props: title, content, user const response = await client.post('/fakeApi/posts', initialPost) return response.data } )y lo llaman en otro archivo asi:
await dispatch(addNewPost({ title, content, user: userId })) En mi proyecto, instalé TypeScript y tipos de reacción ( @types/react ). Aunque el código es JavaScript, esto me da inteligencia de VSCode IDE en tipeos adecuados. Sin embargo, cuando lo hago arriba, veo:
Los tipos esperan 0 argumentos pero obtienen uno, mi objeto. Al pasar el mouse sobre el método addNewPost , veo que no toma argumentos y devuelve la acción asíncrona.
¿Cómo puedo hacer que mi IDE y el soporte de mecanografiados reconozcan los parámetros adecuados requeridos?
Intenté agregar una cadena JSDOC a la función addNewPost creada de esta manera:
/** * addNewPost * @returns {(initialPost:{title:string, content:string, user: string}) => void} the returned action create takes an object as arg */ export const addNewPost = createAsyncThunk( 'posts/addNewPost', async (initialPost) => { const response = await client.post('/fakeApi/posts', initialPost) ...Siguiendo otra sugerencia de Stack Overflow sobre cómo usar JSDocs para describir una función devuelta. pero eso no parece funcionar.
¿Alguien tiene alguna sugerencia?
El problema es que el tipo de Dispatch predeterminado de Redux solo entiende que la función dispatch() puede aceptar objetos de acción simple. No sabe que una función thunk es algo válido que se puede pasar.
Para que este código funcione correctamente, debe seguir nuestras instrucciones para configurar la tienda e inferir el tipo real de dispatch en función de todo el middleware real que se configuró, que generalmente incluiría el middleware thunk:
https://redux.js.org/tutorials/typescript-quick-start
Luego, use ese tipo AppDispatch en otra parte de la aplicación, de modo que cuando intente enviar algo, TS reconozca que los thunks son algo válido para pasar.
Entonces, típicamente:
// store.ts const store = configureStore({ reducer: { posts: postsReducer, comments: commentsReducer, users: usersReducer } }) // Infer the `RootState` and `AppDispatch` types from the store itself export type RootState = ReturnType<typeof store.getState> // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} export type AppDispatch = typeof store.dispatch // hooks.ts import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux' import type { RootState, AppDispatch } from './store' // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = () => useDispatch<AppDispatch>() export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector // MyComponent.ts import { useAppDispatch } from '../../app/hooks' export function MyComponent() { const dispatch = useAppDispatch() const handleClick = () => { // Works now, because TS knows that `dispatch` is `AppDispatch`, // and that the type includes thunk handling dispatch(someThunk()) } } Además, en su caso específico, está usando createAsyncThunk . Debe decirle a TS cuáles son los tipos para sus argumentos, según https://redux-toolkit.js.org/usage/usage-with-typescript#createasyncthunk :
export const addNewPost = createAsyncThunk( 'posts/addNewPost', async (initialPost: InitialPost) => { // The actual `client` in the Essentials tutorial is plain JS // But, if we were using Axios, we could do: const response = await client.post<ResultPost>('/fakeApi/posts', initialPost) // If the `client` was written well, `.data` is of type `ResultPost` return response.data } )Tuve algo de suerte al revisar el JSDoc para que estuviera específicamente por encima del payloadCreator dentro de la función createAsyncThunk de esta manera:
export const addNewPost = createAsyncThunk( 'posts/addNewPost', /** * Make POST request to API w. params and create a new record * @param {{content: string, title: string, user: string}} initialPost * @returns {Promise<{content: string, date: string, id: string, reactions: Object, title: string, user: string}>} returned data */ async (initialPost) => { const response = await client.post('/fakeApi/posts', initialPost) // The response includes the complete post object, including unique ID return response.data } ) A medida que miro más de cerca cómo funciona createAsyncThunk , me doy cuenta de por qué esto tiene más sentido, ya que createAsyncThunk en sí mismo no devuelve estos valores, son parámetros y tipos de retorno de las funciones que le estamos pasando.