I understand that Pick was used for the type of setState because returning undefined for a key that should not be undefined would result in the key being set to undefined by React.
However, using Pick causes other problems. For one, the compiler service's autocomplete becomes useless, as it uses the result of the Pick for autocompletion, and when you request completions the Pick's result doesn't yet contain the key you may want to autocomplete. But the problems are particularly bad when writing setState with a callback argument:
- The list of keys is derived from your first
return statement; if you don't return a particular key in your return statement you also can't read it in the argument without forcing the list of keys to reset to never. Multiple return statements can be hard to write if they return different keys, especially if you have an undefined return somewhere (e.g. if (state.busy) { return }).
- This can be worked around by always using the input in a spread (e.g.
this.setState(input => ({ ...input, count: +input.count + 1 }))) but this is redundant and a deoptimization, particularly for larger states, as setState will pass the return value of the callback to Object.assign.
- If, for some reason, the type you are returning is not compatible with the input type, the
Pick will choose never for its keys, and the function will be allowed to return anything. Even keys that coincide with an existing key effectively allow any as a value -- if it doesn't fit, it's just not Picked, and is treated as an excess property for {}, which is not checked.
- If
never is picked as the generic argument, for any of the reasons listed above, a callback argument may actually be treated as an argument to the object form of setState; this causes the callback's arguments to be typed any instead of {}. I am not sure why this is not an implicit any error.
interface State {
count: string // (for demonstration purposes)
}
class Counter extends React.Component<{}, State> {
readonly state: Readonly<State> = {
count: '0'
}
render () {
return React.createElement('span', { onClick: this.clicked }, this.state.count)
}
private readonly clicked = () => {
this.setState(input => ({
count: +input.count + 1 // not a type error
// the setState<never>(input: Pick<State, never>) overload is being used
}))
}
}
To sum it up, while the use of Pick, despite some inconvenience, help catch type errors in the non-callback form of setState, it is completely counter-productive in the callback form; where it not only does not do the intended task of forbidding undefined but also disables any type checking at all on the callback's inputs or outputs.
Perhaps it should be changed, at least for the callback form, to Partial and hope users know to not return undefined values, as was done in the older definitions.
I understand that
Pickwas used for the type ofsetStatebecause returningundefinedfor a key that should not be undefined would result in the key being set to undefined by React.However, using
Pickcauses other problems. For one, the compiler service's autocomplete becomes useless, as it uses the result of thePickfor autocompletion, and when you request completions thePick's result doesn't yet contain the key you may want to autocomplete. But the problems are particularly bad when writingsetStatewith a callback argument:returnstatement; if you don't return a particular key in your return statement you also can't read it in the argument without forcing the list of keys to reset tonever. Multiple return statements can be hard to write if they return different keys, especially if you have an undefined return somewhere (e.g.if (state.busy) { return }).this.setState(input => ({ ...input, count: +input.count + 1 }))) but this is redundant and a deoptimization, particularly for larger states, assetStatewill pass the return value of the callback toObject.assign.Pickwill chooseneverfor its keys, and the function will be allowed to return anything. Even keys that coincide with an existing key effectively allowanyas a value -- if it doesn't fit, it's just notPicked, and is treated as an excess property for{}, which is not checked.neveris picked as the generic argument, for any of the reasons listed above, a callback argument may actually be treated as an argument to the object form ofsetState; this causes the callback's arguments to be typedanyinstead of{}. I am not sure why this is not an implicit any error.To sum it up, while the use of
Pick, despite some inconvenience, help catch type errors in the non-callback form ofsetState, it is completely counter-productive in the callback form; where it not only does not do the intended task of forbiddingundefinedbut also disables any type checking at all on the callback's inputs or outputs.Perhaps it should be changed, at least for the callback form, to
Partialand hope users know to not returnundefinedvalues, as was done in the older definitions.