Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions packages/toolkit/src/createSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,7 @@ export interface ReducerCreators<State> {
* @public
*/
export type SliceCaseReducers<State> =
| Record<
string,
| CaseReducerDefinition<State, PayloadAction<any>>
| CaseReducerWithPrepareDefinition<
State,
PayloadAction<any, string, any, any>
>
| AsyncThunkSliceReducerDefinition<State, any, any, any>
>
| Record<string, ReducerDefinition>
| Record<
string,
| CaseReducer<State, PayloadAction<any>>
Expand Down Expand Up @@ -692,7 +684,7 @@ export function buildCreateSlice({ creators }: BuildCreateSliceConfig = {}) {
} else {
handleNormalReducerDefinition<State>(
reducerDetails,
reducerDefinition,
reducerDefinition as any,
contextMethods,
)
}
Expand Down
38 changes: 38 additions & 0 deletions packages/toolkit/src/tests/createSlice.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,38 @@ describe('type tests', () => {
expectTypeOf(action.error).toEqualTypeOf<'error'>()
},
),
testInferVoid: create.asyncThunk(() => {}, {
pending(state, action) {
expectTypeOf(state).toEqualTypeOf<TestState>()

expectTypeOf(action.meta.arg).toBeVoid()
},
fulfilled(state, action) {
expectTypeOf(state).toEqualTypeOf<TestState>()

expectTypeOf(action.meta.arg).toBeVoid()

expectTypeOf(action.payload).toBeVoid()
},
rejected(state, action) {
expectTypeOf(state).toEqualTypeOf<TestState>()

expectTypeOf(action.meta.arg).toBeVoid()

expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
},
settled(state, action) {
expectTypeOf(state).toEqualTypeOf<TestState>()

expectTypeOf(action.meta.arg).toBeVoid()

if (isRejected(action)) {
expectTypeOf(action.error).toEqualTypeOf<SerializedError>()
} else {
expectTypeOf(action.payload).toBeVoid()
}
},
}),
testInfer: create.asyncThunk(
function payloadCreator(arg: TestArg, api) {
return Promise.resolve<TestReturned>({ payload: 'foo' })
Expand Down Expand Up @@ -856,6 +888,12 @@ describe('type tests', () => {
>
>()

expectTypeOf(slice.actions.testInferVoid).toEqualTypeOf<
AsyncThunk<void, void, {}>
>()

expectTypeOf(slice.actions.testInferVoid).toBeCallableWith()

expectTypeOf(slice.actions.testInfer).toEqualTypeOf<
AsyncThunk<TestReturned, TestArg, {}>
>()
Expand Down
7 changes: 3 additions & 4 deletions packages/toolkit/src/tests/createSlice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ describe('createSlice', () => {
initialState: [] as any[],
reducers: (create) => ({
thunkReducers: create.asyncThunk(
function payloadCreator(arg, api) {
function payloadCreator(arg: string, api) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... This brings up the question: will this still be okay in an untyped .js file for non-TS-users or will it make it unusable?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's the same behaviour as createAsyncThunk. if they're not using type checking on their JS there will be nothing to complain, and if they are then they'll need to annotate:

const getPost = createAsyncThunk(
  'posts/getPost',
  /**
   *
   * @param {number} id
   * @returns {Promise<Post>}
   */
  async (id) => {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/posts/${id}`,
    )
    const data = await response.json()
    return data
  },
)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can even set the ApiConfig:

const getPost = createAsyncThunk(
  'posts/getPost',
  /** @type {import("@reduxjs/toolkit").AsyncThunkPayloadCreator<Post, number, { state: RootState }>}*/
  async (id, api) => {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/posts/${id}`,
    )
    const data = await response.json()
    return data
  },
)

return Promise.resolve('resolved payload')
},
{ pending, fulfilled, rejected, settled },
Expand Down Expand Up @@ -722,7 +722,7 @@ describe('createSlice', () => {
reducers: (create) => ({
thunkReducers: create.asyncThunk(
// payloadCreator isn't allowed to return never
function payloadCreator(arg, api): any {
function payloadCreator(arg: string, api): any {
throw new Error('')
},
{ pending, fulfilled, rejected, settled },
Expand Down Expand Up @@ -765,7 +765,7 @@ describe('createSlice', () => {
initialState: [] as any[],
reducers: (create) => ({
thunkReducers: create.asyncThunk(
function payloadCreator(arg, api) {
function payloadCreator(arg: string, api) {
return 'should not call this'
},
{
Expand Down Expand Up @@ -833,7 +833,6 @@ describe('createSlice', () => {
slice.actions.thunkReducers.rejected(
new Error('test'),
'fakeRequestId',
{},
),
),
).not.toThrow()
Expand Down