Learning TypeScript with React - Part 1
Ana Liza Pandac
Posted on July 1, 2019
⚡TL;DR: Understanding what are types, type annotations, why use them and how to use them can help you catch errors during development while also enhancing code quality and readability.
Two weeks ago, I decided to pick up TypeScript so I started learning it by creating TEDFlix. Built together with React, it fetches the videos from the official Tedx Talks Youtube channel for you to watch. Why TEDFlix? It sounds fun and honestly, I ran out of ideas 😛
In this two-part series, I wanted to share what I learned from doing this project.
Before diving into it, let me give you a short definition of what is TypeScript (TS) taken from the official TS website.
“TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.”
Now, let me also give you a high level overview of the app’s features and how the React side is structured.
It is a one page app where it has a video player, a list of related talks and a search bar which are all built as separate React components. On page load, it fetches a default list of talks from a JSON file instead of fetching from Youtube to avoid exceeding the daily usage quota of the Youtube API when the page is loaded by many users. The app only communicates with Youtube when searching for talks.
Okay, let's start.
First, the project setup
I generated the project using Create React App with TypeScript
create-react-app my-app-name --typescript
How did I use TypeScript in this app?
Since I’m a TS beginner, I get started by learning the syntax and features of the language first specifically the types, type annotations and interfaces. In this first post, I'm gonna be talking about types and annotations only.
What is a type?
Since TypeScript is a typed language, it means we can specify/annotate the type of the variables, function parameters and object properties.
From my own understanding, a type is a symbol/representation of all the properties and methods that a variable has access to. It means if we added a type of number
to a variable, the TS compiler knows that the variable has and can only access all the properties and methods that a number has.
A type is annotated to the variable, parameter or property using this format
:type
.
For example,let name: string
.
There are a lot of available types in TS however, in this app, these are the ones that I've used:
- Primitive Types - number, string, boolean
-
Arrays - by adding
[]
after the type (e.g.let names: string[]
) - Video - an interface or a custom type I created to represent a video object's properties
See here for a more comprehensive list of types.
Why do we care about annotating types?
- Types are one of the best forms of documentation you can have. This is a very helpful to the next developer who has to read your code which could also be the future you.
- It helps the TypeScript compiler help you. If you annotate a variable as a number and a few lines later assigned it a value which is a string, the compiler will give you the following error.
let x: number = 123;
x = '123'; // Error: cannot assign a `string` to a `number`
Even if we remove the :number
annotation from variable x
, the same error will be given by the compiler because TypeScript is smart*.
*If you declare a variable with a value without specifying the type, TypeScript will figure out the type of the variable based on the initial value assigned to it.
This is called type inference.
"So if type inference is present then why do I have to bother annotating the variables?"
Type inference does not work all the time.
An example scenario is when you delay the initialization of a variable as shown below.
let age;
// a few lines later
age = 12;
// The compiler will NOT give an error even
// if we assign a non-numeric value to `age`
age = 'Ana';
If you delay the variable initialization without annotating a type to it, the variable will automatically have a type of any
associated with it since the TS compiler does not have any idea what kind of properties age
will have later on.
Variables with a type of any
should be avoided as much as possible. It defeats the purpose of the idea behind the TS type system where we want to catch as many errors as possible during development. Why? Because, the compiler cannot do error checking on types of any
.
Remember, use types to help the TS compiler help you.
How is type annotation used in the app?
Aside from using types on the variable declarations, I also used annotations in the function declarations.
A function can be annotated in the following ways:
1. Arrow function
const add = (a:number, b:number):number => {
return a + b;
}
// or
const add: (a: number, b: number) => number = (a, b) => {
return a + b;
};
2. Non-arrow function
function add(a:number, b:number):number {
return a + b;
}
In both of these examples, the compiler will give an error if the function returns a string or any type that is not a number since it is expecting a number.
For fetching the channel's videos, I created a function called fetchChannelVideos
which accepts a boolean flag indicating whether to fetch the default videos from the JSON file and a search query string. Both of these are optional parameters (by adding ?
after the property name) which are represented as an interface. I will explain later what an interface is but for now let’s take a closer look on how the function is annotated.
interface FetchVideosArguments {
shouldUseDefaultVideos?: boolean;
searchQuery?: string;
}
export const fetchChannelVideos: (
args: FetchVideosArguments
) => Promise < Video[] > = async ({
shouldUseDefaultVideos = false,
searchQuery
}) => {};
On the left side of the assignment operator (=
),
const fetchChannelVideos: (args: FetchVideosArguments) => Promise <Video[]>
we are annotating the variable fetchChannelVideos
that was declared not the function assigned. We are telling the TS compiler that this variable will have a function assigned to it with these types of arguments and return value.
While the right part of the =
is the function assigned.
async ({
shouldUseDefaultVideos = false,
searchQuery
}) => {};
To annotate the function itself, we have to specify its arguments and their types and the return value as well.
So why didn't I annotate the function to assigned to
fetchChannelVideos
? Because again, TypeScript is smart.
Seeing that I assigned the function to a variable that was annotated, it is able to infer that the function will have the same argument names and types and the return value as well, otherwise it will give an error if I add or specify different argument names or return a different value.
*The function arguments and return value is inferred
However, if I’m exporting the function directly without assigning it to a variable then I have to annotate it like below.
export async function fetchChannelVideos({
shouldUseDefaultVideos = false,
searchQuery
}: FetchVideosArguments): Promise<Video[]> {
// function body
}
Okay, now that we have an idea about the what, why and how of type annotations, there's one last question.
Where do we use types?
The answer is: everywhere. By using TypeScript, every data in your application will have a type associated with it whether you like it or not.
Closing Thoughts
I have only touched the basics so far but I’ve already seen how helpful it is, seeing the errors the compiler gives while writing the code instead of discovering them later when running the app saved me a lot of time.
Learning Resources
If you want to learn more and join me on this journey, here are some available resources that can help you get started.
Official TypeScript Documentation
I deployed the app in Netlify: https://tedflix.netlify.com/
I will share the Github repo of the project soon.
*Update: 06/07*
Last part about interfaces is up 🎉
Learning TypeScript with React - Part 2 (The what, why and how of interfaces)
Ana Liza Pandac ・ Jul 6 '19
Let me know in the comments below if you have any feedback and suggestions or pointers on where to go next. Thank you in advance.
Cheers!
Posted on July 1, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
January 9, 2023