Using spread operator or Object.assign will lose strict typescript check.
type State = {
key1: string;
key2: string;
};
function App() {
const [state, setState] = React.useState<State>({
key1: "",
key2: ""
});
React.useEffect(() => {
setState(prevState => {
return { ...prevState, ...{ key3: "" } };//no ts error
});
setState(prevState => {
return Object.assign({}, prevState, { key3: "" });//no ts error
});
setState({key1:'', key2:'', key3: ''});//ts error!!
}, []);
return <div>{JSON.stringify(state)}</div>;
}
I've try with Vanilla TS, still problem
type Person = {
name: string,
age: number
}
const person1:Person = {...{name: 'a', age: 20, salary: 20}};//no error
const person2:Person = Object.assign({name: 'a', age: 20, salary: 20});//no error
const person3:Person = {name: 'a', age: 20, salary: 20};//ts error
typescript playground link
It seems there are some bugs with ts. I want to know if there is a simple way to update the state with strict type checking? Could anyone guide me through this?
Yes, this seems to be a known issue from within the Typescript community. Check this discussion on Github out
There is a workaround for this. Reference to it can be found here.
TLDR;
You basically type your new state before merging the two to form a new one. Two correctly typed states will result in a correctly typed state.
Take a look at this snippet:
(() => {
type State = { foo: string };
const state: State = { foo: 'bar' };
const assignedValues: State = { foo: 1 }; //complain expected
const newState: State = Object.assign({}, state, assignedValues)
})();
From this, this is what I reckon your new code will look like:
const [state, setState] = React.useState<State>({
key1: "",
key2: ""
});
React.useEffect(() => {
setState(prevState => {
const valueToChange: Partial<State> = {
key1: "I am sure that this is typed",
}
return Object.assign({}, prevState, valueToChange);
});
}, []);
return <div>{JSON.stringify(state)}</div>;
iamfeek's answer (assign the new state to a variable/constant with the correct type in order to typecheck it, then merge in from that variable/constant) is correct as far as it goes, but you'll probably want to add Partial
to your real use case.
setState(prevState => {
const merge:Partial<State> = { key3: "" } }; // Flags up the error
return { ...prevState, ...merge }; // Or return Object.assign({}, prevState, merge);
});
Partial<State>
allows the merge object to only have a subset of State
's properties, but doesn't let it have incorrect properties.
Perhaps even give yourself a utility function for it:
function mergeState(state:State, merge:Partial<State>) {
return { ...prevState, ...merge };
}
then
setState(prevState => {
return mergeState(prevState, { key3: "" } ); // Flags up the error
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With