I've started using TypeScript with React, and after a while, learning, and implementing them together. This is how I feel:
@ndm_haskell I use React + TypeScript daily and have never looked back. It is so enjoyable.10:25 AM - 08 Sep 2018
I couldn't believe how much my DX has improved. I'll leave you the main features I enjoy the most so hopefully you will feel excited to give it a shot 😃 !
Disclaimer ⚠️
This is not a beginner-friendly introduction to TypeScript, you need to know at least how to use Interfaces, however, at the end of the article I will drop a list of great resources I found very useful along with the fastest way to get React + TypeScript up and running and start hacking:
$ npx create-react-app my-app --template typescript
# or
$ yarn create react-app my-app --template typescript
TLDR;
Pros:
- Code completion and type validation with type definitions.
- Great community and learning resources.
Cons:
- TypeScript syntax and concepts like Generics can be hard to understand.
Table Of Contents
- IntelliSense
- Type Declarations
- Type Validation
- Hooks
-
The Bad parts.Trade-offs ⚖️ - Resources
- Conclusion
IntelliSense
I use VSCode and one of its best features is IntelliSense (A.K.A Code Completion), TypeScript takes advantage of it.
Let's say we have a simple List
component, and we declare the Props
Interface, in this case one prop labels
, an Array of strings:
// List.tsx
import React from 'react';
interface Props {
labels: string[]
}
// We tell TypeScript the parameter type
// + Object destructuring 👇
function List({ labels }: Props): React.Element {
return (
<ul>
{ labels.map((label, index) => {
<li key={index}>{label}</li>
)}
</ul>
);
}
const strings = ['React', 'TypeScript', '❤️'];
<List labels={strings}/>
This is what we get:
If we type labels.
inside List
we get the code completion with methods and attributes from Array.prototype
, in this case Array.prototype.forEach() and then inside the callback
function we get all methods and attributes from String.prototype
, in this example Array.prototype.toUpperCase().
VSCode is written in TypeScript, it has a built-in type definitions for Native Objects, and the best part? We can declare our own types, and most libraries we use come along with their own type definitions files already 😃 !
Which bring us to the next great feature:
Type Definitions
React and TypeScript community is awesome, they've created a huge centralized repository of High Quality Type definitions files:
DefinitelyTyped / DefinitelyTyped
The repository for high quality TypeScript type definitions.
Most libraries keep their type declarations files on this repo, and sometimes we have to install them independently, on the official website we can search and find instructions to install them, in the case of React, for example:
$ yarn add @types/react --save-dev
How can we use them?
import React from 'react';
interface Props {
labels: string[]
}
function List({ labels }: Props): React.Element {
const styles: React: React.CSSProperties = {
backgroundColor: 'blue'
}
// ...
}
Let's say we want to write some inline CSS, in this case we can use the React built-in type definition React.CSSProperties
, it'll show us an error if we write a typo like backgroundKolor
and we'll get code completion for the properties 😁.
And we can also see the source code so we get used to read and write them.
We already learned how take advantage of type definitions to get Code Completion, but now we'll learn another powerful feature that comes with them:
Type Validation
What if we either don't pass the right prop type or we don't pass it at all?
interface Props {
labels: string[]
}
function List({ labels }: Props) {
// ...
}
const strings = ['React', 'TypeScript', '❤️'];
const numbers: number[] = [1, 2, 3];
// 1) We pass an array of numbers
<List labels={numbers} />
// 2) We don't pass it
<List />
In both cases we get an Error 🚫:
These errors are clear, they tell us what's the type error
, where that declaration
was made, if the prop is actually required, and this applies to functions also (although React Components are just functions 😉).
The TypeScript compiler won't be happy until we pass the right parameters, this is useful to see possible bugs ahead even before compiling the code and checking the browser.
What about prop-types?
Yes, we can achieve the same validation using prop-types
:
import React from 'react';
import PropTypes from 'prop-types';
function List({ labels }) {
// ...
}
List.propTypes = {
labels: PropTypes.arrayOf(PropTypes.string).isRequired
}
However, since prop-types
checks our code during runtime, we have to compile it first, see the actual error on the console, and also, this validation just happens on development mode 😟, whereas TypeScript analyses our code statically.
Hooks
When it comes to React Hooks, useState
for example, TypeScript can be super handy, specially with type validations.
function Counter() {
// we can also use brackets <> syntax for types declarations:
const [counter, setCounter] = useState<number>(0);
const add = () => {
// this is gonna give us an error 😱
setCounter('string');
}
return(
<div>
<button onClick={add}>+</button>
{counter}
</div>
);
}
In this way we make sure every time we update the state
we preserve the value type, this can save us hours of debugging and headaches.
The Bad parts. 🤐 Trade-offs
We learned how TypeScript can benefit the whole team when it comes to write components, just let's imagine we write our (ideally) reusable components library with well defined types and our colleague import one of them, they are gonna see beforehand:
- Props types and if they are required or not.
- Code completion for prop names.
This can save us time browsing source code to make sure we pass the right data to every component.
But we also know that in Software Development there is no Silver Bullet. Every tool we choose comes with a trade-off, in the case of TypeScript, of course there are some:
Reading TypeScript can be hard:
interface Array<T> {
concat(...items: Array<T[] | T>): T[];
reduce<U>(
callback: (state: U, element: T, index: number, array: T[]) => U,
firstState?: U
): U;
// ···
}
But don't worry, I got this snippet from this great article which explains every detail, that example was really confusing for me at first.
Some concepts can be complicated to grasp:
- Interfaces.
- Generics.
- Interfaces vs Types.
These new concepts (especially if members of our team aren't used to them) can bring more questions and confusion.
Of course we can tackle them, it just depends on our team members experience, available time, and feeling eager to learn new things.
This article reflects the idea I've been thinking about for a while and it's likely to be my next post topic, everything in Software Development is a trade-off
:
You Might Not Need Redux. People often choose Redux before they… | by Dan Abramov | Medium
Dan Abramov ・ ・
Medium
Resources
These are the best resources that helped me to understand and love TypeScript:
- Understanding TypeScript’s type notation
-
typescript-cheatsheets / react
Cheatsheets for experienced React developers getting started with TypeScript
- TypeScript Tutorial for JS Programmers Who Know How to Build a Todo App
- TypeScript Generics for People Who Gave Up on Understanding Generics
Conclusion
TypeScript comes with a lot of benefits and constrains, but our context (team, priorities, goals) can define if we can get more advantages than cons and make our lives easier!
IMHO when it comes to large scale applications, and specially if we are building a bullet-proof architecture or System Design, the effort pays off, we can write less bugs-prone code and deliver features faster, and safer.
Thanks for reading, if you found this article useful please follow me on Twitter and let me know what you think!
Happy coding!
Top comments (9)
Nice post! I like using the the
React.FC
type for functional components. YourList
component would look like this:React.FC
adds achildren
prop which we don't want in this example.Excellent post Diego! very helpful
Gracias por redactar esta info ~
(vi que tu primer post está en español jaja)
Great article! I was especially interested since I tried out TypeScript without much background on it in a brand new project at work a few months ago. I ~70% loved it, there were just some really annoying parts I came across that I don't know were other from my naivety or just trade-offs with TypeScript:
Quickly prototyping a component or a helper function is very annoying having to be very explicit with everything, since I'm just trying to get an idea or concept down.
I don't know if I ever fully grasped Generics. My understanding of it is that if you pass a type to an instance of the component/function that uses the generic, that should be the type of passed. In numerous components and functions, I tried to do something like this (ex: a Carousel component that can take both an array of strings or numbers as an "items" prop, or a reduce function that takes an object with a nested array, and it should flatten that array). I just felt like I was constantly having to import/export interfaces and declare them as types on component props or function arguments, and no pun intended, prevented me from keeping them "generic".
Mapping data received from server into the shape that was desired for my pages/components. I found myself having to first declare an interface with the shape of the data received from the server, and then would have to declare yet another interface with the returned value(s) from my mapping. This would turn into an even bigger mess if there were nested objects that had to be transformed. It just seemed so unnecessary to do since I was using this data once after receiving it from fetch, but simply setting any to it is just cheating TypeScript.
Like I said, some of this could just be me not being a full pro in TS, so I appreciate the Resources here to see maybe if I can further my understand. Was interested if you came across any similar issues to me! Thanks
Just the post I was looking for I will be trying TypeScript with React later.
Great article!!
to me the pros and cons it bring is nearly equal, not really worth it
Great article, thanks!