TypeScript with React Tutorial
Tags: typescript, react, beginners, tutorial
Published: 2022-06-25 18:58:56

![React TypeScript Tutorial](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hapxyspv5ofi24c3ejln.png)

### Introduction

TypeScript has grown to be one of the most popular and widely used programming language in web development. TypeScript is a superset of JavaScript. That simply means TypeScript is essentially JavaScript with some additional features. The goal of TypeScript is to write strongly typed JavaScript. Strong typing helps to scale web applications with minimal bugs. TypeScript code is converted to JavaScript by use of a compiler like TypeScript Compiler or Babel.

For a better understanding, we will develop a Student Application using React and TypeScript. This tutorial will give you everything you need to get started with TypeScript in your next project.

If you would prefer to follow this tutorial on YouTube it's available at the link below.

{% youtube Lwi2qGqA164 %}

The final solution is available on GitHub 
{% github https://github.com/tndungu/React-TypeScript-StudentApp %}

### Prerequisites

This tutorial assumes you have some basic knowledge of [React](https://reactjs.org/)

**Why use TypeScript?**

There are many benefits of using typescript. The main ones are listed below:

- Strong typing ensures bugs are caught during development as opposed to being caught while the application is in Production. Also makes it easy to debug code.
- Documentation - It serves as documentation for JavaScript code making it easy to read and maintain.
- Saves development time. 
- Generics in TypeScript provides a powerful type system that gives developers a lot of flexibility.

### Student App in TypeScript

We will build an app using React that will cover the following aspects of TypeScript.

- Props 
- Type inference vs Type annotation
- Union Types
- Organizing interfaces
- Enums 
- Generics

### App Development: Step by Step Guide

To start a new typescript app, use the following command
- **yarn**:
```TypeScript
yarn create-react-app student-app --template typescript
```
- **npm**:
```TypeScript
npx create-react-app student-app --template typescript
```

cd into student-app and `yarn start` OR `npm start` if using npm. 

#### Props
We will start by passing a prop to the `<App/>` component. It will be a string that will have the name of the app. Here we will see our first use case for TypeScript.
Modify the `App.tsx` and `index.tsx` files to look as below. Delete the `App.test.tsx` file.

```TypeScript
//Index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
root.render(
  <React.StrictMode>
    <App name="Student App" />
  </React.StrictMode>
);

//App.tsx
export interface AppName {
  name: string;
}

function App({ name }: AppName) {
  return (
    <div className="App">
      <h1>{name}</h1>
    </div>
  );
}

export default App;
```
On your browser, you should be able to see Student App displayed. As we can see, **AppName** is an **Interface** that has a property called name. 
An **Interface** is an abstract type that the compiler uses to know which property names a given object can have. It's used for type checking. 
In the `AppName` Interface, the property name is a string, that's why we are passing a string to the `App` component. If you try to pass any other type like integer or boolean the App will give an error. It will not even compile. If you were not using TypeScript, the app will compile but give unexpected results later after deployment. 

#### Type inference vs Type annotation

```TypeScript
const [studentId,setStudentId] = useState(0)

setStudentId('test')
```
If you try the code above, it will not compile, typescript will give an error. This is because typescript has already inferred the type of `studentId` to be an integer(number). If you try to assign a string to `studentId` will therefore generate an error at compile time. This is referred to as **type inference** in TypeScript. 

Similarly, the code below will not run in TypeScript.

```TypeScript
const [student, setStudent] = useState(null)

setStudent({
  name: 'Antony',
  surname: 'Ndungu',
  age: 15
})
```
This is because TypeScript infers the student object to be of type null and therefore we have to explicitly define the student object and in the `useState()` hook we have to tell TypeScript user can be either null or student object. This is referred to as **Type annotation**. We will do that using the `Student` interface. The final code of our `App.tsx` will look as follows:

```TypeScript
import { useState } from "react";
import './App.css'

export interface AppName {
  name: string;
}

export interface Student {
  name: string;
  surname: string;
  age?: number;
  address?: {
    houseNo: number;
    street: string;
    Town: string;
  }
}

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20
    })
  }
  
  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      <button onClick={addStudent}> Add Student</button>
    </div>
  );
}
export default App;

```
From the above code, the student can either be `null` or `Student` object. This is denoted by the code `useState<Student | null>(null)`. This introduces another concept called **Union Types**.

#### Union Types

This is when you have an object that can be of having different types. For instance you might have `const [student, setStudent] = useState<Student | null | boolean>(null)`. In this case `Student | null | boolean` are Union Types.

#### Organizing Interfaces

There are 2 issues as far as our interfaces are concerned:

- We should not nest objects as we have done in the `Student` interface. Instead, we should have another interface for Address.
- The Interfaces should be on their separate module for ease of maintenance and re-use. 

We will create a new Interface for `Address`. We will then create a new module for interfaces by creating an `interfaces.ts` file inside the `src` folder and moving the interfaces there. We will then import our interfaces in the `App.tsx` file. The final `App.tsx` and `Interfaces.ts` files will look as follows:

```TypeScript
//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName } from './interfaces'

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20
    })
  }

  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      <button onClick={addStudent}> Add Student</button>
    </div>
  );
}

export default App;

//interfaces.tsx
export interface AppName {
  name: string;
}

export interface Address {
  houseNo: number;
  street: string;
  Town: string;
}

export interface Student {
  name: string;
  surname: string;
  age?: number;
  address?: Address
}

```
#### Enums
An **Enum** is a type for holding constant values. In our example, the student level can either be "Undergraduate" or "Postgraduate".

```TypeScript
export enum Level {
  Undergraduate = "Undergraduate",
  Postgraduate = "Postgraduate"
}

```
The above enum can be used to conditionally display the age of a student based on the student's level as shown below:

```TypeScript
{
  student?.level === Level.Undergraduate &&
  <p><b>Age: {student.age}</b></p>
}
```

#### Generics

Generics are an important feature of TypeScript that is used for creating reusable components. The same component can be used to handle different data types as shown below. 

**Display both Students' and Courses' lists using the same component.**

![TypeScript Student Courses Lists Display](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nfeoayhp7442svlkasd2.png)
 
For our student App, I would like to display 2 lists: One for the students' list and another one for the courses' list. Without generics, I will end up creating 2 components that will be used to display the 2 lists. However, with Generics I will use only one component to display both lists. The `DisplayData` component can be re-used to display any list of items even as our app grows bigger.

In `src` folder, I have created `DisplayData.tsx` component. The file looks as follows:

```TypeScript
interface Item {
  id: number;
}

interface DisplayDataItem<T> {
  items: Array<T>
}

export const DisplayData = <T extends Item>({ items }: DisplayDataItem<T>) => {
  return (
    <>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{JSON.stringify(item)}</li>
        ))}
      </ul>
    </>
  )
}

```

Interface `Item` has a property `id` which means any object that uses this component must have an `id` property. Interface `DisplayDataItem<T>` is an object that represents an `Array<T>` of type `T` which means it can be used by any object which consists of an array of items. `DisplayData` is a function that accepts an array of items and displays the list. 
The following is the final code for `App.tsx`, `App.css` and `data.ts` files.

```TypeScript

//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName, Level } from './interfaces'
import { studentList, coursesList } from "./data";
import { DisplayData } from "./DisplayData";

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)

  const addStudent = () => {
    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20,
      level: "Undergraduate"
    })
  }
  
  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      {student?.level === Level.Undergraduate &&
        <p><b>Age: {student.age}</b></p>
      }
      <button onClick={addStudent}> Add Student</button>
      <h3>List of Students</h3>
      <div>
        <DisplayData items={studentList} />
      </div>
      <h3>List of Courses</h3>
      <div>
        <DisplayData items={coursesList} />
      </div>
    </div>
  );
}

export default App;

//data.ts
export const studentList = [
  { id: 1, name: 'Antony', surname: 'Ndungu', level: 'Undergraduate', age: 20 },
  { id: 2, name: 'Chanelle', surname: 'John', level: 'Postgraduate', age: 50 },
  { id: 3, name: 'Ian', surname: 'Smith', level: 'Undergraduate', age: 46 },
  { id: 4, name: 'Michael', surname: 'Starke', level: 'Postgraduate', age: 64 },
  { id: 5, name: 'Chris', surname: 'De Kock', level: 'Undergraduate', age: 19 },
]

export const coursesList = [
  { id: 1, code: 'A141', name: 'Algorithms Analysis', description: 'Analysis & Design' },
  { id: 1, code: 'BN445', name: 'Computer Architecture I', description: 'Computer Architecture' },
  { id: 1, code: 'P888', name: 'Operations Research', description: 'Maths - Operations Research' },
  { id: 1, code: 'Z9989', name: 'Discrete Maths', description: 'Discrete Mathematics' }
]

.App {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  flex-direction: column;
}

li{
  list-style-type: none;
}

button {
  height: 30px;
  width: 150px;
  background-color: turquoise;
  border-radius: 5px;
}

```

**Generic Search Function**

Finally, we will add a Generic search where the student list can be sorted based on either student name or Age on a button click. 
Create a `GenericSort.ts` file and ensure you have the following code. This code takes a list of array items and key for sorting then returns the sorted list. For example, if I would like to sort the student's list based on student name I will call the function `GenericSort(studentList,"name")`
This is a powerful use case for generics, I can use it if I want to sort the student records list based on different sorting columns. Implementing this without TypeScript would end up with many functions that are difficult to extend.

```TypeScript
//GenericSort
export const GenericSort = <T,>(items: Array<T>, key: keyof T) => {

  items.sort((a, b) => {
    if (a[key] > b[key]) {
      return 1;
    }
    if (a[key] < b[key]) {
      return -1;
    }
    return 0;
  })
  return items
}
```

```TypeScript
//App.tsx
import { useState } from "react";
import './App.css'
import { Student, AppName, Level } from './interfaces'
import { studentList, coursesList } from "./data";
import { DisplayData } from "./DisplayData";
import { GenericSort } from "./GenericSort";

function App({ name }: AppName) {
  const [student, setStudent] = useState<Student | null>(null)
  const [list, setList] = useState(studentList)

  const addStudent = () => {

    setStudent({
      name: 'Antony',
      surname: 'Ndungu',
      age: 20,
      level: "Undergraduate"
    })
  }

  const sortData = () => {

    GenericSort(studentList, "age")
    setList([...studentList])
  }
  return (
    <div className="App">
      <h1>{name}</h1>
      <p><b>{student?.name} {student?.surname}</b></p>
      {student?.level === Level.Undergraduate &&
        <p><b>Age: {student.age}</b></p>
      }
      <button onClick={addStudent}> Add Student</button>
      <br />
      <button onClick={sortData}>Sort Data</button>
      <h3>List of Students</h3>
      <div>
        <DisplayData items={list} />
      </div>
      <h3>List of Courses</h3>
      <div>
        <DisplayData items={coursesList} />
      </div>
    </div>
  );
}

export default App;

```
### Conclusion

Awesome! at this point, you have gone through the core building blocks of building a React App using TypeScript. In the final part of our project, we went through an introduction to some advanced features of TypeScript, Generics. 
Happy coding with TypeScript.
Feel free to comment below in case you need further assistance.