我正在按照 Redux Essentials Tutorials 学习如何使用
createAsyncThunk
来生成 Thunk。在他们的例子中他们创建了一个像这样的thunk:
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
}
)
他们在另一个文件中这样称呼它:
await dispatch(addNewPost({ title, content, user: userId }))
在我的项目中,我安装了打字稿和反应类型(
@types/react
)。即使代码是 JavaScript,这也可以让我从 VSCode IDE 获得正确类型的智能感知。然而,当我执行上述操作时,我看到:
输入需要 0 个参数,但得到一个,我的对象。将鼠标悬停在该方法上
addNewPost
我看到它不需要参数并返回异步操作。
如何让我的 IDE 和打字稿类型支持识别所需的正确参数?
尝试将一些 JSDOC 字符串添加到创建的
addNewPost
函数中,如下所示:
/**
* 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)
...
遵循另一个关于如何使用 JSDocs 描述返回函数的 Stack Overflow 建议。但这似乎不起作用。
大家有什么建议吗?
问题是 Redux 中默认的
Dispatch
类型只能理解 dispatch()
函数可以接受普通的操作对象。它不知道 thunk 函数是可以传入的有效函数。
为了使此代码正常工作,您需要按照我们的说明设置商店并根据配置的所有实际中间件(通常包括 thunk 中间件)推断 dispatch
的
real类型:
https://redux.js.org/tutorials/typescript-quick-start
然后,在应用程序的其他位置使用该
AppDispatch
类型,这样当您尝试发送某些内容时,TS 就会识别出 thunk 是可以传入的有效内容。
所以,通常:
// 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())
}
}
此外,在您的具体情况下,您正在使用
createAsyncThunk
。您需要告诉 TS 其参数的类型,按照 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
}
)
我很幸运地将 JSDoc 修改为专门位于
payloadCreator
函数内的 createAsyncThunk
参数之上,如下所示:
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
}
)
当我更仔细地观察
createAsyncThunk
的工作原理时,我意识到为什么这更有意义,因为 createAsyncThunk 本身不返回这些值,它们是我们传递给它的函数的参数和返回类型。
在 VSCode 上,我设法使用 IntelliSense(描述和参数名称)正确显示所有内容的唯一方法是执行以下操作:
/**
* @callback MyAction
* @param {string} testParam - A test param
*/
/**
* Does something.
* @type {MyAction}
*/
export const myAction = createAsyncThunk("MY-ACTION", async (testParam) => {
// ...
});
相比之下,这就是我将 JSDoc 放在
payloadCreator
之上时得到的结果:
export const myAction = createAsyncThunk("MY-ACTION",
/**
* Does something.
* @param {string} testParam - A test param
*/
async (testParam) => {
// ...
}
);