Class Components

Within TypeScript, React.Component is a generic type (aka React.Component<PropType, StateType>), so you want to provide it with (optional) prop and state type parameters:

type MyProps = {
// using `interface` is also ok
message: string;
};
type MyState = {
count: number; // like this
};
class App extends React.Component<MyProps, MyState> {
state: MyState = {
// optional second annotation for better type inference
count: 0,
};
render() {
return (
<div>
{this.props.message} {this.state.count}
</div>
);
}
}

View in the TypeScript Playground

Don't forget that you can export/import/extend these types/interfaces for reuse.

Why annotate state twice?

It isn't strictly necessary to annotate the state class property, but it allows better type inference when accessing this.state and also initializing the state.

This is because they work in two different ways, the 2nd generic type parameter will allow this.setState() to work correctly, because that method comes from the base class, but initializing state inside the component overrides the base implementation so you have to make sure that you tell the compiler that you're not actually doing anything different.

See commentary by @ferdaber here.

No need for readonly

You often see sample code include readonly to mark props and state immutable:

type MyProps = {
readonly message: string;
};
type MyState = {
readonly count: number;
};

This is not necessary as React.Component<P,S> already marks them as immutable. (See PR and discussion!)

Class Methods: Do it like normal, but just remember any arguments for your functions also need to be typed:

class App extends React.Component<{ message: string }, { count: number }> {
state = { count: 0 };
render() {
return (
<div onClick={() => this.increment(1)}>
{this.props.message} {this.state.count}
</div>
);
}
increment = (amt: number) => {
// like this
this.setState((state) => ({
count: state.count + amt,
}));
};
}

View in the TypeScript Playground

Class Properties: If you need to declare class properties for later use, just declare it like state, but without assignment:

class App extends React.Component<{
message: string;
}> {
pointer: number; // like this
componentDidMount() {
this.pointer = 3;
}
render() {
return (
<div>
{this.props.message} and {this.pointer}
</div>
);
}
}

View in the TypeScript Playground

Something to add? File an issue.

Last updated on by krishnaUIDev