The more I use TypeScript, the more I come to like it – similarly this is how I feel about React! So I started using the two of them together to get code suggestions and error highlighting before transpile, but also self-documenting components which is a huge benefit. Let's look at a small example on how to get up and running with React and TypeScript.
There are a few ways to do this – for brevity we'll use the CRA method with the optional TypeScript flag. But you can also add TypeScript to an existing React app by requiring the necessary libraries.
npx create-react-app todos-typescript --typescript
The first thing you'll notice is the .js
extensions have been replaced with .tsx
which is a TypeScript file type, with JSX in it (hence the "x").
In a new file src/interfaces/Todo.ts
, let's create an interface to represent our todo
object:
// Notice it's a .ts file!
interface Todo {
id: number,
title: String,
completed: Boolean
}
export default Todo
This will make our interface reusable throughout our tiny project. Now in our App.tsx
file, let's create interfaces for our Props and State:
import TodoInterface from './interfaces/Todo'
interface IProps {}
interface IState {
todos: Array<TodoInterface> // This is the interface we created earlier!
}
Great! If you're new to TypeScript, this tells any component using the state
object that the todos
property is an array of objects, each containing an id
, title
, and completed
property. How is that for self-documenting code?!
Next, we need to tell the App
component that our state should adhere to this interface:
class App extends React.Component<IProps, IState> { // Small change here!
state = {
todos: [
{
id: 1,
title: 'Walk the dog',
completed: 'no' // Let's make this a String on purpose
}
]
}
render() {
return (
<div className="App">
...
</div>
);
}
}
Right away, we can see our IDE (if you have the correct extensions) telling us that our state doesn't adhere to our interface:
To mitigate this, just updatecompleted: 'no'
to completed: false
so it's a Boolean value as expected.
Next, we'll create a new Todos
component to which we'll pass our state as props. But instead of just trusting we've typed it correctly, we'll create the props interface and have TypeScript watch our back!
import TodoInterface from './interfaces/Todo'
interface IProps {
todos: Array<TodoInterface>
}
class Todos extends React.Component<IProps> { // Use the interface!
render() {
return this.props.todos.map(todo => {
return (
<div>
<h3>{todo.title}</h3>
<span>Completed: {todo.completed ? 'Yes' : 'No'}</span>
</div>
)
})
}
}
Lastly, we can start our development server to view our terribly-basic Todo app which doesn't offer any interaction options but does have self-documenting, reliable code!
And that's it! Very small example of integrating two great libraries together – hopefully you can see the benefit in using them to write applications at scale!