More accurate typing of Object.assign and React component setState()
This is a proposal for solving the second use case mentioned in Issue #6218
Problem
/** * This class is a given by the React framework */ class ReactComponent<S> { protected _state: S; /** * This method actually accepts any supertype of S */ protected setState(newState: S): void { for (let name in newState) { if (newState.hasOwnProperty(name)) { this._state[name] = newState[name]; } } } protected componentWillMount(): void { // abstract } } /** * Some state interface declaration. Note all members are optional to allow setState to * be called with supertypes of BaseState */ interface BaseState { a?: string; } /** * My own base class for certain React widgets */ class BaseWidget<S extends BaseState> extends ReactComponent<S> { constructor() { super(); this._state = {}; } protected componentWillMount(): void { this.setState({ a: "boo" }); } }
$ tsc v1.ts
v1.ts(39,9): error TS2322: Type '{}' is not assignable to type 'S'.
v1.ts(43,23): error TS2345: Argument of type '{ a: string; }' is not assignable to parameter of type 'S'.
The compiler cannot know that the setState() method will accept any super-type of S, i.e. an object with any subset of the members of S.
Proposal
Add a keyword to type setState() properly. To avoid confusion, I chose partof instead of e.g. 'supertype of' (see also initial confusion in #6218). For me, 'partof' conveys that I can give the method any object with a subset of the members of S.
/** * This class is a given by the React framework */ class ReactComponent<S extends {}> { protected _state: S; /** * This method accepts any supertype of S */ protected setState(newState: partof S): void { } }
Properties of partof
- Only works for object types (hence the 'extends {}' above) because I wouldn't know how to pass part of e.g. a string
- Allows any supertype of the given type
- Incorporates knowledge of the generic parameter. Given the class declaration
class ReactComponent<S extends { foo: number; }>, one is able to callsetState({ foo: 3})andsetState({})but notsetState({ bar: 3 })inside the class definition.
Comments more than welcome, I'm not a compiler expert. This is just to get the discussion going. If you think there already exists a way of typing this, please check with the original issue for a couple of failed examples.