I tried to ask this earlier generically (Why can't I assign a U to a Partial<T> when T extends U?) and I think I now understand why S extends {dirty: boolean}
is incorrect (S could be {dirty: false}
, but most of the suggestions were to rewrite the function in some way. So, how do I rewrite this function so that it has proper type checking and doesn't require a cast?
import {ActionCreatorWithPreparedPayload} from '@reduxjs/toolkit';
import {assert, pick} from '@helpmedo/util';
import {State, ThunkAction} from '@helpmedo/state';
export const createAsyncUpdateAction = <Arg, S extends {dirty: boolean}, P>(
updateAction: ActionCreatorWithPreparedPayload<[Arg, Partial<S>], P>,
selector: (state: State, arg: Arg) => S,
) => (
arg: Arg,
update: Partial<S>,
asyncUpdate: () => Promise<unknown>,
): ThunkAction<unknown> => async (dispatch, getState) => {
const state = selector(getState(), arg);
assert(!state.dirty, 'Attempt to modify dirty state');
const save = pick(state, Object.keys(update));
dispatch(updateAction(arg, {...update, dirty: true}));
try {
const result = await asyncUpdate();
dispatch(updateAction(arg, {dirty: false} /* as Partial<S> */));
return result;
} catch (err) {
dispatch(updateAction(arg, {...save, dirty: false}));
throw err;
}
};
The existing error is Argument of type '{ dirty: false; }' is not assignable to parameter of type 'Partial<S>'.ts(2345)
on the line with the commented out cast.