I'd like to know how I can make Typescript understand, that update can be mutated by the function which is passed as the second parameter to modifyState because update will be cloned.
// Basic example
import { clone } from 'lodash';
interface IState {
foo: string;
}
const state: Readonly<IState> = {
foo: "bar"
};
// Start situation
function modifyState<S>(
prevState: S,
updateFunc: (update: S) => void
): S {
const newState = clone(prevState);
updateFunc(newState);
return newState;
}
modifyState(
state,
update => {
update.foo = "another string"; // 'Cannot assign to 'foo' because it is a constant or a read-only property.'
}
);
You could infer the generic type from the generic type parameter of the received Readonly<T>
value instead:
function modifyState<S>(
prevState: Readonly<S>, // <- this one
updateFunc: (update: S) => void
): Readonly<S> {
...
}
I believe the code is self-explanatory. If you need to support both readonly
and non-readonly versions, you could use an overload signature, or a union type, such as:
prevState: Readonly<S> | S
In this case, the TypeScript compiler will prefer Readonly<S>
over S
, when applicable.