Skip to content

Commit 70ada97

Browse files
committed
use class for RejectWithValue wrapper, move rejectWithValue to thunkApi
1 parent 29dc0c9 commit 70ada97

File tree

3 files changed

+36
-40
lines changed

3 files changed

+36
-40
lines changed

etc/redux-toolkit.api.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,6 @@ export function createAsyncThunk<Returned, ThunkArg = void, ThunkApiConfig exten
128128
}>;
129129
};
130130

131-
// @public (undocumented)
132-
export namespace createAsyncThunk {
133-
var // (undocumented)
134-
rejectWithValue: <RejectValue>(value: RejectValue) => RejectWithValue<RejectValue>;
135-
}
136-
137131
// @alpha (undocumented)
138132
export function createEntityAdapter<T>(options?: {
139133
selectId?: IdSelector<T>;
@@ -290,11 +284,7 @@ export type SliceCaseReducers<State> = {
290284
export { ThunkAction }
291285

292286
// @alpha (undocumented)
293-
export function unwrapResult<T>(returned: {
294-
error: any;
295-
} | {
296-
payload: NonNullable<T>;
297-
}): NonNullable<T>;
287+
export function unwrapResult<R extends ActionTypesWithOptionalErrorAction>(returned: R): PayloadForActionTypesExcludingErrorActions<R>;
298288

299289
// @alpha (undocumented)
300290
export type Update<T> = {

src/createAsyncThunk.ts

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ import { FallbackIfUnknown } from './tsHelpers'
1111
// @ts-ignore we need the import of these types due to a bundling issue.
1212
type _Keep = PayloadAction | ActionCreatorWithPreparedPayload<any, unknown>
1313

14-
export type BaseThunkAPI<S, E, D extends Dispatch = Dispatch> = {
14+
export type BaseThunkAPI<
15+
S,
16+
E,
17+
D extends Dispatch = Dispatch,
18+
RejectedValue = undefined
19+
> = {
1520
dispatch: D
1621
getState: () => S
1722
extra: E
1823
requestId: string
1924
signal: AbortSignal
25+
rejectWithValue(value: RejectedValue): RejectWithValue<RejectedValue>
2026
}
2127

2228
/**
@@ -36,21 +42,8 @@ const commonProperties: Array<keyof SerializedError> = [
3642
'code'
3743
]
3844

39-
const rejectionSymbol: unique symbol = Symbol('rejectWithValue')
40-
type RejectWithValue<RejectValue> = {
41-
[rejectionSymbol]: 'reject'
42-
value: RejectValue
43-
}
44-
function rejectWithValue<RejectValue>(
45-
value: RejectValue
46-
): RejectWithValue<RejectValue> {
47-
return {
48-
[rejectionSymbol]: 'reject',
49-
value
50-
}
51-
}
52-
function isRejectWithValue(value: any): value is RejectWithValue<unknown> {
53-
return value && value[rejectionSymbol] === 'reject'
45+
class RejectWithValue<RejectValue> {
46+
constructor(public readonly value: RejectValue) {}
5447
}
5548

5649
// Reworked from https://github.com/sindresorhus/serialize-error
@@ -100,13 +93,15 @@ type GetDispatch<ThunkApiConfig> = ThunkApiConfig extends {
10093
type GetThunkAPI<ThunkApiConfig> = BaseThunkAPI<
10194
GetState<ThunkApiConfig>,
10295
GetExtra<ThunkApiConfig>,
103-
GetDispatch<ThunkApiConfig>
96+
GetDispatch<ThunkApiConfig>,
97+
GetRejectValue<ThunkApiConfig>
10498
>
99+
105100
type GetRejectValue<ThunkApiConfig> = ThunkApiConfig extends {
106101
rejectValue: infer RejectValue
107102
}
108103
? RejectValue
109-
: undefined
104+
: unknown
110105

111106
/**
112107
*
@@ -163,7 +158,7 @@ export function createAsyncThunk<
163158
const aborted = error && error.name === 'AbortError'
164159
return {
165160
payload,
166-
error: miniSerializeError(error),
161+
error: miniSerializeError(error) || { message: 'Rejected' },
167162
meta: {
168163
arg,
169164
requestId,
@@ -207,10 +202,13 @@ export function createAsyncThunk<
207202
getState,
208203
extra,
209204
requestId,
210-
signal: abortController.signal
205+
signal: abortController.signal,
206+
rejectWithValue(value: RejectedValue) {
207+
return new RejectWithValue(value)
208+
}
211209
})
212210
).then(result => {
213-
if (isRejectWithValue(result)) {
211+
if (result instanceof RejectWithValue) {
214212
return rejected(null, requestId, arg, result.value)
215213
}
216214
return fulfilled(result, requestId, arg)
@@ -237,16 +235,24 @@ export function createAsyncThunk<
237235
fulfilled
238236
})
239237
}
240-
createAsyncThunk.rejectWithValue = rejectWithValue
238+
239+
type ActionTypesWithOptionalErrorAction =
240+
| { error: any }
241+
| { error?: never; payload: any }
242+
type PayloadForActionTypesExcludingErrorActions<T> = T extends { error: any }
243+
? never
244+
: T extends { payload: infer P }
245+
? P
246+
: never
241247

242248
/**
243249
* @alpha
244250
*/
245-
export function unwrapResult<T>(
246-
returned: { error: any } | { payload: NonNullable<T> }
247-
): NonNullable<T> {
251+
export function unwrapResult<R extends ActionTypesWithOptionalErrorAction>(
252+
returned: R
253+
): PayloadForActionTypesExcludingErrorActions<R> {
248254
if ('error' in returned) {
249255
throw returned.error
250256
}
251-
return returned.payload
257+
return (returned as any).payload
252258
}

type-tests/files/createAsyncThunk.typetest.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const defaultDispatch = (() => {}) as ThunkDispatch<{}, any, AnyAction>
2424
})
2525
.addCase(async.rejected, (_, action) => {
2626
expectType<ReturnType<typeof async['rejected']>>(action)
27-
expectType<Error>(action.error)
27+
expectType<Partial<Error> | undefined>(action.error)
2828
})
2929
)
3030

@@ -110,8 +110,8 @@ const defaultDispatch = (() => {}) as ThunkDispatch<{}, any, AnyAction>
110110
{
111111
rejectValue: RejectValue
112112
}
113-
>('books/fetch', async arg => {
114-
return createAsyncThunk.rejectWithValue<RejectValue>({ data: 'error' })
113+
>('books/fetch', async (arg, { rejectWithValue }) => {
114+
return rejectWithValue({ data: 'error' })
115115
})
116116

117117
const returned = await defaultDispatch(fetchBooksTAC(1))

0 commit comments

Comments
 (0)