The release of TypeSciprt 1.6 includes support for React components. I decided to give it a whirl and see what TypeScript has to offer.
The end result is a port of the Redux TodoMVC example to TypeScript. (See my repo on GitHub)
So why use types in the first place? Isn’t the dynamic and flexible nature of JavaScript what makes it so great in the first place? As strange as it sounds, I actually believe that adding more constraints to the system can result in more freedom. Of course, the type system has to be powerful and flexible enough to not get in your way.
This post is meant for developers who work with JavaScript and has an interest in TypeScript and React. I will be showing examples using the Redux framework, but it is not a requirement. No prior knowledge of TypeScript is required, but you should have some familiarity with React.
I will cover the following topics:
- What are types, and why we need them?
- How TypeScript can help us develop React applications.
- Additional considerations when choosing TypeScript and React.
typings
as opposed to tsd
, which is the direction the community has adopted.
Types are good for you
If you have experience in Java (or in a similar statically typed language), you may be immediately turned off by types. The first thing to note is that a type in TypeScript is not a class. A more mathematical definition of a type is that a type is a name given to the set of inputs and outputs of a given function. That is, a type describes the interface of a function. This is the way I think about types.
Say I have a function defined as follows:
The add
function above says that it takes in two parameters a
and b
both of type number
, then outputs
a number
. Thus, if I write any of the following statements, I will get a compile error.
If you are using an IDE that supports TypeScript you will get editor hints as you are typing.
As seen above, types gives us two benefits.
- Catch errors early on during compile time, not runtime.
- Serves as documentation for functions. And in editors that support TypeScript, we get type hints as we code.
Expanding on point #2, the type hints are especially useful for options object, as seen in libraries like jQuery.
Say I have this function defined below.
As a user of doSomething
I might wonder what options I am allowed to pass in. With the Options
type defined
below, I will get hints in our editor as I code.
More on types
Before we move on to React examples, I want to expand on types a bit more.
As mentioned earlier, types in TypeScript are not classes. The following defines a Todo
type, but does not create a class.
The id?
syntax means that the id
property is optional (since a new Todo
may not have an ID yet).
Given the above definition, I can now do the following.
What is more interesting is that I can use the type to form an interface over a function that operate over Todo
s.
Again, you will see errors in editors that support TypeScript as you are typing.
Function types
You can even define types for functions.
Union types and type guards
The last thing I will show is union types. A variable of an union type can be assign any type within that union.
Notice that there are no interfaces or subclasses defined above for numberOrString
. Union types can be combined with type
guards to help manage branches within a function.
The above features are enough for us to jump into the TodoMVC application. If you want to learn more about TypeScript, I found the Handbook to be a useful resource.
TodoMVC in React, Redux, and TypeScript
By now you have seen some of the benefits of using a powerful type system. But how does this fit in with React?
With TypeScript 1.6, you can now write your React components in TSX files. The following is an example from my TodoMVC example.
Note: Since React does not provide TypeScript definitions,
we cannot use it without providing ambient module definitions (e.g. .d.ts
files).
In this case, I am using a tool called typings
to install the missing definitions.
You can view the typings.json
file to see all of the definitions I have installed.
Properties interface vs React.PropTypes
Properties interface differs from React.PropTypes
in that the latter will only give you errors during runtime. With
Properties interface we can get feedback immediately from the compiler.
Unfortunately, as of this writing Visual Studio Code (the editor I am using) does not seem to support TSX files yet. The above errors were from Webpack with ts-loader.
Types and Redux
In the TodoMVC example, I defined a Todo
type that is used throughout the application. It is used as the return
type of
actions/todos
. The state of
reducers/todos
is Todo[]
. And the
components
all take in
Todo
or Todo[]
as properties.
This ensures that as I am coding, my actions and reducers (stores) all work with the correct types.
I now benefit from all the type hints and errors when working with my actions and reducers.
Using union types as models
Let’s add an initializing state to the todos
reducer by introducing a Model
union type for it.
We’ll have to change our reducer’s state from type Todo[]
to Model
, and add type guards to our
reduce functions.
The type guard ensures that in the else
branch, state
is of type Todo[]
, which is why the above code compiles.
Initializing
, but used an interface instead. The
downside of this approach is that you cannot use state instanceof Initializing
in the type guard since
interfaces are not available for reflection during runtime. It's up to you how you want to implement your own types.
Rendering the initializing state
Within my App
component, I can use the same Model
and type check function to render an initializing state.
The alternative to this approach might be to change our state to the following type.
This trades in the union type for a boolean flag to let us know why todos
is undefined
or null
. This makes
our state much harder to reason about when we add in more and more flags and metadata. From my experience with real-world
applications, doing this type of thing is hard to avoid without types.
This initialization example is a bit contrived, but I hope it shows the power behind the technique. I have a branch of that shows how the above code works in the TodoMVC app. Fair warning, it is not completely functional because I only did enough work to get a few examples.
Closing Remarks
In this post I offered a glimpse of how TypeScript can help you when writing a React application. Some benefits you will receive are:
- Type hints as you code (in editors that support TypeScript).
- Type errors during compile time, or as you code in supported editors.
- Guarantees against certain classes of errors when your application compiles successfully. (typos, wrong
props
usage, etc.) - Union types to simplify application state.
Does this mean you should rewrite your React application in TypeScript right now? It’s up to you. There are downsides in choosing TypeScript.
- Your favourite editor may not support TypeScript. Best editors right now would be WebStorm or Visual Studio Code IMO.
-
As of this writing, Visual Studio Code does not support TSX files (at least from my observation).(As Franck pointed out in the comments, you can point the
typescript.tsdk
option in your VSCsettings.json
file to the lib directory of your TypeScript install) - You will need to invest in more tools (TSD, ts-loader for Webpack, etc.).
If you think the benefits outweigh the the costs, definitely give TypeScript a go!
Resources
- TodoMVC in React and Redux
- TypeScript Handbook
- TypeScript and JSX
- Redux (Flux-like framework)
- Hello World with TypeScript and JSX (This one helped me debug some errors)