I'd like to use immer
in my react typescript application.
One of my reducer should deal with a state with a generic argument.
The generic argument can be anything (basic type, array, object, ...)
How to update such property within a produce method ?
Here's a minimal repro code:
import { produce } from 'immer';
type SomeState<TResult> = {
inner: TResult
}
type SetInner<TResult> = {
type: 'SET_INNER';
newValue: TResult;
}
const reducer = <TResult>(
state: SomeState<TResult>,
action: SetInner<TResult>
): SomeState<TResult> => {
return produce(state, draft => {
switch (action.type) {
case 'SET_INNER':
draft.inner = action.newValue;
break;
}
});
}
This code fails on the line draft.inner = action.newValue;
with this error:
Type 'TResult' is not assignable to type 'Draft'.
How to fix that ?
To clarify, I'd like these use cases to be possible:
// Basic type
const initialState = {
inner: "foo"
};
const action: SetInner<string> = {
type: 'SET_INNER',
newValue: "bar"
}
const newState = reducer(initialState, action);
console.log(newState);
// Object type
type Point = { x: number, y: number }
const initialState2 = {
inner: { x: 10, y: 4 }
};
const action2: SetInner<Point> = {
type: 'SET_INNER',
newValue: { x: -5, y: 14 }
}
const newState2 = reducer(initialState2, action2);
console.log(newState2);
I think answer of @captain-yossarian from Ukraine should be corrected.
The behavior below is considered harmful since you are introducing an implementation detail here and some cases it won't work...
TResult -> Draft<TResult>
castDraft
is the no-op utility function for this case.
//old one
draft.inner = action.newValue
//new one
draft.inner = castDraft(action.newValue)
For further reading: https://immerjs.github.io/immer/typescript#cast-utilities