In React, the Component definition looks something like this:
class Component<S> {
state:S;
setState(state:S):void;
}
And you define a component like this:
interface MyState {
name: string;
age: number;
}
class MyComponent extends Component<MyState> { }
Now the issue I have is that in the React API setState
is supposed to be called with a partial state object, representing properties to be updated, like this:
setState({name: "Aaron"});
Note that age
is not declared. The problem I have is that TypeScript doesn't allow this, it gives an assignment error like Property 'age' is missing
. By my understanding, the react.d.ts
definition is wrong in this sense. But is there a solution?
I tried this:
setState({name: "Aaron"} as MyState);
But this gives the same error, even though it works in the Playground without giving an error. Why does it work in the Playground? Any ideas?
"Partial types" are still missing in TypeScript currently, see TypeScript issue #4889 as well as this related question. I'm afraid it's not yet possible to make this type-check correctly.
You might get away with marking all fields of your MyState
interface as optional (by adding ?
modifiers), but that in turn weakens the type-checking for things like Component.state
(where you want all fields to be set).
EDIT (December 2016): TypeScript 2.1 introduces mapped types, which supports describing partial types using Partial<T>
! Now you can use the following type definition for Component
:
class Component<S> {
state: S;
setState(state: Partial<S>) : void;
}
The react.d.ts
definition is indeed wrong, and cannot be fixed until Partial Types are supported.
The problem is that setState
takes a partial state, which is a different type from normal state. You can solve this by manually specifying both types:
interface MyState {
name: string;
age: number;
}
interface PartialMyState {
name?: string;
age?: number;
}
And manually declaring MyComponent
without extending the provided React Component
class:
class MyComponent {
state: MyState;
setState(state:PartialMyState): void;
//...
}
Which means you'll have to duplicate these function definitions for every subclass of Component
in your code. You may be able to avoid this by defining a correct Component
class generalized by an additional type of partial state:
class CorrectComponent<S,P> { // generalized over both state and partial state
state:S;
setState(state:P):void;
//...
}
class MyComponent extends CorrectComponent<MyState,PartialMyState> { }
You'll still have to write a partial version for every state type you have.
Alternatively, you can make setState
non-typesafe by changing its argument's type to Object
.
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