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);
CodePudding user response:
Please consider this example:
import { produce, Draft } from 'immer';
type SomeState<TResult> = {
inner: TResult
}
type SetInner<TResult> = {
type: 'SET_INNER';
newValue: Draft<TResult>; // <--------------- CHANGE IS HERE
}
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; // ok
break;
}
}
});
}
// 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 have wrapped newValue
into Draft
type. According to source code, Draft
states for:
/** Convert a readonly type into a mutable type, if possible */
type Draft<T>=...