A Complete Guide to Using TypeScript in Web Development with React, Create React App, and Next.js.
Introduction
TypeScript becomes very popular for web development. To help you get started with TypeScript in web development, this article slowly introduces the basic TypeScrip, its usage for building web pages, and its integration with React.
- π Set Up a TypeScript Web Development Environment: code, demo.
- π Set Up a TypeScript React Web Development Environment: code.
What Is TypeScript and Why Does It Matter
TypeScript offers all of JavaScriptβs features, and an additional layer on top of these: TypeScriptβs static-typing system .β¦ The main benefit of TypeScript is that it can highlight unexpected behavior in your code, lowering the chance of bugs.
β TypeScript for JavaScript Programmers
TypeScript is a statically-typed language (such as Java) where variable types are known at compile time and cannot be changed at any point. In contrast, JavaScript is a dynamically-typed language where the variable types can be changed. TypeScriptβs type system can easily and quickly help developers spot variable type-related bugs during development. So there will be much fewer run-time errors.
TypeScript has been a demand in the job market. TypeScript not only helps developers reduce the chances of bugs, but also makes teamwork much easier as developers easily understand the intention of the variables and methods created by other developers on the team. So knowing how to work with TypeScript will help you apply for more good jobs.
Prerequisites
Install Node on your computer
The reason we need Node is that both TypeScript development and runtime environment require Node. There are several ways of installing TypeScript, and we will use a dependency managerβββnpm or yarn
to download TypeScript in this guide. To run the package, we also need a Node
environment. Where to install is totally up to usβββeither installing globally (download and install the LTS version) or installing locally using nvm
(Node.js Version Manager) to avoid version conflicts among various projects.
Install TypeScript Compiler (TSC) installed on your computer
Why we need TSC simply because TypeScript cannot be understood by browsers, so it has to be compiled into JavaScript by the TS. Similarly, we could choose to install TSC globally or locally (e.g. on a per-project basis).
Use TypeScript Globally
You need to install the TypeScript compiler globally on your machine
Step 1: Install TypeScript Compiler Globally on Your Machine
# Install TypeScript
$ npm i -g typescript
# or
$ yarn global add typescript
# Check the version
$ tsc --version // or tsc -v
Step 2: Create a TypeScript File
Run the command from your terminal:
vim index.ts // for vim user
touch index.ts // for Mac user
And paste the below code to the fileβββindex.ts
interface User {
name: string;
id: number;
}
const user: User = {
name: "Hayes", // fixed
id: 0,
};
Step 3: Compile the TypeScript File to a JavaScript File
Run the following command to compile and type-check a TypeScript file to a JavaScript file with the same name:
tsc index.ts
If you want to specify the name of the output file, you could run the command:
tsc index.ts --outfile new-file-name.js
Tip: to --noEmit
type checks your project without compiling anything.
Use TypeScript Within a Project
The benefit of setting up a TypeScript on a per-project basis lets you have many projects with many different versions of TypeScript, which keeps each project working consistently.
Step 1: Create a TypeScript Project
Create a project directory:
$ mkdir typescript-project$ cd typescript-project
Install TypeScript within the project
$ npm i typescript β save-dev
# or
$ yarn add typescript --dev
The compiler is installed in thenode_modules
directory and can be run with npx tsc
.
We use TypeScript only for development purposes. We will compile TypeScript to JavaScript for production purposes.
Now our TypeScript project structure looks like this:
typescript-project
β -- package-lock.json
β -- package.json
typescript
is added to the devDependencies
(package.json)
{
βdevDependenciesβ: {
βtypescriptβ: ββ΄.8.4β
}
}
Initialize TypeScript project:
$ npx tsc β init
The tsc
is the built-in TypeScript compiler. Running tsc
will compile TypeScript code into JavaScript. Using the --init
flag initializes a TypeScript project by creating a tsconfig.json
file in the root project directory. Running tsc
locally will compile the project defined by thetsconfig.json.
Running tsc --noEmit
typechecks the project without compiling anything.
Step 2: Compile the TypeScript Project
Create a TypeScript file in the project root directory β index.ts
interface User {
name: string;
id: number;
}const user: User = {
username: "Hayes", // Error
id: 0,
};
Notice the Type error is highlighted in our text editor:
If we try to compile our code using the command:
$ tsc index.ts
It fails due to the Type error in the above:
Fix the index.ts
interface User {
name: string;
id: number;
}const user: User = {
name: "Hayes", // fixed
id: 0,
};
Run tsc index.ts
again, then you will see the compiled JavaScript file in the root directory:
$PROJECT_ROOT
βββ package.json
βββ package-lock.json
Now, you only see a defined user object in the index.js
file:
Step 3: Set Up the TypeScript Config File (tsconfig.json) in the Root Directory of Your Project
The presence of a
tsconfig.json
file in a directory indicates that the directory is the root of a TypeScript project. Thetsconfig.json
file specifies the root files and the compiler options required to compile the TypeScript-based projects.
Run command:
$ tsc --init
A tsconfig.json
file will be automatically created in the root directory as the following:
$PROJECT_ROOT
βββ tsconfig.json
|ββ package.json
βββ package-lock.json
This is the generatedtsconfig.json
file as the following:
A TSConfig Base example:
Before we move on to web development using TypeScript in the next section, letβs have some upfront knowledge about TypeScript syntax, especially its typing system.
Prepare TypeScript Knowledge for Web Development
Three main primitives
- boolean –
true
orfalse
- number –
12
or12.34
- string –
hello world
TypeScript Type and Interface
interface User {
name: string;
id: number;
}// or using types
types User = {
name: string;
id: number;
}const user: User = {
name: "Hayes",
id: 0,
};
Two main ways TypeScript assigns a type:
- Explicit Type β easier to read and more intentional
let name: string = βtableβ;
- Implicit Type β shorter, faster to type, and often used when developing and testing
let name = βtableβ;
Union Type β Assigning More Than One Type
A union type is used when a value is more than one type.
let price: string | number;
price = 100;
price = '100';
TypeScript Type Arrays
let ids: number[] = [1, 2, 3, 4, 5];
let names: string[] = ['table', 'chair', 'lamp'];
let results: boolean[] = [true, false, false];
TypeScript Type Objects
Objects in TypeScript must have both the correct properties and value types:
interface User {
name: string;
id: number;
}const user1: User = {
firstName: "Hayes", // Error - not correct properties
id: 0,
};const user2: User = {
name: "Hayes",
id: "1234", // Error - not correct value type
};
TypeScript Type Function
TypeScript allows us to define the argument types as well as the return type.
function getItemPrice(name: string, price: number): string {
return `The price of ${name} is ${price} dollars`;
}
console.log(typeof(getItemPrice('apple', 20)) // string
Arrow function- add a return type after the function arguments, separated by a colon (:
).
const getItemPrice = (name: string, price: number): string => {
return `The price of ${name} is ${price} dollars`;
}
Error In Type Assignment
let name: string = βtableβ; // type string
name = 33; // attempts to re-assign the value to a different type
Tip: Please refer to the TypeScript Documentation and Handbook to learn more about TypeScript.
The best way to understand how to use TypeScript is to combine it with a project. So, in the next sections, we will integrate TypeScript into different web development environments to build a simple web page.
Set Up a Web Development Environment for a Node.js+TypeScript Project Without Frameworks
Step 1: Create the Project Folder Structure
$ mkdir typescript-web-dev $ cd typescript-web-dev $ mkdir src dist
The project structure looks like this:
$PROJECT_ROOT
βββ dist
βββ src
Step 2: Initiate the Node.js Project
Run yarn init
β°β yarn init
yarn init v1.22.19
question name (typescript-web-dev):
question version (1.0.0):
question description:
question entry point (index.js):
question repository url:
question author:
question license (MIT):
question private:
success Saved package.json
β¨ Done in 17.88s.
or yarn init -y
to
β°β yarn init -y yarn init v1.22.19 warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications. success Saved package.json β¨ Done in 0.04s. Now project structure looks like the below: $PROJECT_ROOT βββ dist |ββ src βββ package.json
Start a Webpack Dev Server with NPM scripts by adding the soon-to-be-installedWebpack-dev-server
to the scripts in the package.json file:
// ./package.json
"scripts": {
"dev": "webpack-dev-server --mode development",// or "webpack serve",
"build": "webpack --mode production",
},
Tip: Command yarn dev
spins up the webpack dev server automatically in watch mode. You will see the GIF demo by the end of this section.
Step 3: Install TypeScript and WebPack Dependencies
yarn add -D typescript ts-loader
ts-loader
is a TypeScript loader for webpack. To make it work, we need TypeScript installed as well.
- Install webpack and webpack dev server
yarn add -D webpack webpack-cli webpack-dev-server
Step 4: Config Webpack and TypeScript
// ./webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/main.ts",
output: {
filename: "bundle.js", // all js files are bundled into this single file
path: path.resolve(__dirname, "dist"),
},
devtool: "source-map",
devServer: {
static: "./dist",
port: 9000, //default port: 8080
},
module: {
rules: [
{
test: /\.ts$/,
use: "ts-loader", // TypeScript loader
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".ts", ".js"],
},
};
Note: In development mode with the webpack dev server, there is no bundle.js file in the dist folder but ONLY in memory. To create the bundle.js file in the dist folder, run yarn build
. You will see the GIF demo by the end of this section.
Run tsc -init
or manually create tsconfig.json in the project root.
// ./tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "es6",
"target": "es5",
"allowJs": true
}
}
Step 5: Prepare Files to Be Served by Webpack Dev Server
./src/main.ts
const appContainer = document.getElementById("app");
const el = document.createElement("div");
el.innerHTML = `
<h1>Hello World</h1>
<span style="background-color: red; color: white;">
This is a web app built without frameworks
</span>
`;
appContainer?.appendChild(el);
Now project structure looks like this:
$PROJECT_ROOT
βββ dist
| βββ index.html
βββ src
β βββ main.ts
βββ webpack.config.js
βββ node_modules
βββ tsconfig.json
βββ package.json
βββ yarn-lock.json
If you run yarn build
(webpack β mode production), you will notice bundle.js
inside the dist
directory. And the project structure becomes:
$PROJECT_ROOT
βββ dist
| βββ index.html
| βββ bundle.js.map
| βββ bundle.js
βββ src
β βββ main.ts
|ββ webpack.config.js
|ββ node_modules
|ββ tsconfig.json
|ββ package.json
βββ yarn-lock.json
./dist/bundle.js
(()=>{var e=document.getElementById("app"),n=document.createElement("div");n.innerHTML='\n <h1>Hello World </h1>\n <span style="background-color: blue; color: white;">\n This is a web app built without frameworks\n </span>\n ',null==e||e.appendChild(n)})();
//# sourceMappingURL=bundle.js.map
Step 6: Run the App β Serving Single Webpage Using Webpack Dev Server
- Run
yarn dev
-> open browser (http://localhost:9000/)-> make changes to code -> changes display on web page immediately. - Run
yarn build
-> Bundle file show up in this directory.
π GitHub Code
Since React is one of the most popular front-end frameworks used in the web development industry, so letβs continue our next stage β integrating TypeScript in a React project to create type-safe React components and their benefits.
Prepare TypeScript Knowledge for React Project Development
Typing React Props Using TypeScript
You can use either Types or Interfaces to type Props and State in React, so which one should we use?
A rule of thumb using Types or Interface in React provided by react-typescript-cheatsheet doc:
- always use the
interface
for public API’s definition when authoring a library or 3rd party ambient type definitions. - consider using
type
for your React Component Props and State.
But in reality, most developers prefer to use an interface to specify prop types. So, the choice is up to you based on your working requirements.
An example using the interface
to type React props:
// src/components/person.tsx
import React from 'react';interface PersonProps {
name: string;
age: number;
skills: string[];
}const Person: React.FC<PersonProps> = ({ name, age, skills }) => {
return (
<div>
<div>{name}</div>
<div>{age}</div>
<div>Skills<ul>{ skills.map((skill) => <li>{skill}</li>}</ul></div>
</div>
);};
export default Person;
Consume the Person
component:
// src/index.tsximport React from 'react';
import Person from './components/person';
const App: React.FC = () => {
return (
<div>
<Person name='Fish' age={40} skills={['Programming', 'Writing', 'Storytelling'}/>
</div>
);
};
export default App;
There are many other properties you could define inside Props. Here is a Props example as a reference:
export declare interface AppProps {
children?: React.ReactNode;
childrenElement: JSX.Element;
style?: React.CSSProperties;
onChange?: React.FormEventHandler<HTMLInputElement>;
props: Props & React.ComponentPropsWithoutRef<"button">;
props2: Props & React.ComponentPropsWithRef<MyButtonWithForwardRef>;
}
Typing React Hooks Using TypeScript
// useState<T>()
const [count, setCount] = useState<number | null>(1);// createContext<T>(), useContext()
const ScrollContext = React.createContext<ScrollValue>({
scrollY: 0
})// useRef<T>()
const { scrollY } = useContext(ScrollContext)
const divRef = useRef<HTMLDivElement>(null)
Tip: Please visit the React TypeScript Cheatsheet to learn more about React TypeScript.
With the knowledge of React+TypeScript, we could continue working on a React project. There are two approaches to creating a React project β starting from scratch or leveraging a React framework. To have a better understanding of the setup of React, letβs learn how to create a React project without using any frameworks in the next section.
Set Up a Web Development Environment for a React TypeScript Project
There will be several steps same to creating a TypeScript web dev project in the above.
Step 1: Create the TypeScript React Project Folder Structure
$ mkdir typescript-react-web-dev
$ cd typescript-react-web-dev
$ mkdir src dist
Step 2: Initiate the Node.js Project
yarn init -y
Add the below scripts to package.json
"scripts": {
"dev": "webpack-dev-server --mode development",
"build": "webpack --mode production",
},
Step 3: Install TypeScript, React, and WebPack Dependencies
yarn add -D typescript @typescript-eslint/parser
yarn add react react-dom
yarn add -D @types/react @types/react-dom
yarn add -D @babel/core babel-loader @babel/preset-react @babel/preset-typescript
yarn add -D webpack webpack-cli webpack-dev-server
Step 4: Config WebPack, Babel, TypeScript
./webpack.config.js
const path = require("path");
module.exports = {
entry: "./src/main.tsx",
output: {
filename: "bundle.js", // all js files are bundled into this single file
path: path.resolve(__dirname, "dist"),
},
devtool: "source-map",
devServer: {
static: "./dist",
port: 9000, //default port: 8080
},
module: {
rules: [
{
test: /\.tsx?$/,
use: {
loader: 'babel-loader',
},
exclude: /node_modules/,
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".jsx],
},
};
./babel.config.json
{
"presets": [
"@babel/preset-typescript",
"@babel/preset-react"
]
}
./tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "umd",
"lib": ["ESNext", "dom"],
"jsx": "react",
"noEmit": true,
"sourceMap": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"moduleResolution": "node",
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true
},
"include": ["src"]
}
Step 5: Prepare Files to Be Served by Webpack Dev Server
./src/main.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
createRoot(document.getElementById('app'))
.render(<App />);
./src/App.tsx
import React from 'react';
export const App = () => (
<h1>TypeScript + React</h1>
);
Step 6: Run the App
Run yarn dev
to start our app in the local environment:
π GitHub Code
Our app works fine in modern browsers, but if you want to work in old engines, then you need to use two tools β Transpilers
and Polyfills
. Please refer to these two articles for further understanding β Transpile and Polyfill to ES5 and Understanding Transipers and Polyfills.
Today many frameworks support TypeScript out of the box, such as Create React App β TS docs, Next.js β TS docs, and Gatsby β TS Docs. Let us bring the above React-TypeScript knowledge to another React project generated by creat-react-app
(You could refer to Using react-beautiful-dnd With Next.js and Typescript to learn a coding project built by Next.js using TypeScript. ).
Set Up a Web Development Environment for a React TypeScript Project Using creat-react-app
Create a React Project With TypeScript
Using a boilerplate comes in handy when you just start learning how to use TypeScript in a React project.
yarn create react-app typescript-react-app --template typescript
Project Structure
$PROJECT_ROOT
βββ public
|ββ src
| βββ App.css
| βββ App. tsx
| βββ index.css
| βββ index.tsx
βββ tsconfig.json
βββ package.json
βββ yarn.lock
The tsconfig.json
will be taken care by create-react-app
but you can customize it to satisfy your needs. For instance, set the production distribution folder to the root production
Run the App in a Development Environment
npm start
# or
yarn start
Note that the development build is not optimized and the bundled js file is in memory. When youβre ready to deploy to production, running npm run build
or yarn build
will create an optimized build of your app in the build folder.
You can customize where to store your production distribution code inside tsconfig.json
Project structure for the compiled React app:
$PROJECT_ROOT
βββ build
| βββ static
| |-- css
| |-- js
| βββ media
|ββ src
βββ public
Create a Web Development Environment for a React TypeScript Project Using Next.js
Create a Next.js Project
Run the following command to generate a Next.js project with pre-installed dependencies including react, react-dom
, next
, eslint
, eslint-config-next
, typescript
, @types/react
, @types/node
, @types/react-dom
.
yarn create next-app --typescript typescript-nextjs-app
Project Structure
$PROJECT_ROOT
βββ pages
β βββ _app.tsx
β βββ index.tsx
β βββ api
β βββ hello.ts
βββ styles
β βββ Home.module.css
β βββ globas.css
βββ node_modules
βββ next-env.d.ts
βββ next.config.js
βββ tsconfig.json
βββ README.md
βββ package.json
βββ yarn.lock
Start the App
Run yarn dev
to start the next dev
command:
If you want to find out more examples/tutorials about using TypeScript in other React frameworks such as Next.js, you might want to check out this article:
Use react-beautiful-dnd With Next.js and TypeScript
Conclusion
I hope you would start trying out using TypeScript in your next React project. In the beginning, you might feel writing in TypeScript slow down your working process, but it pays off for sure.
Thanks for reading! Happy coding!π
Useful Sources and Links
- React+TypeScript Cheatsheets β An up-to-date information on React and TypeScript.
- Create React TypeScript Project With Webpack and Babel β An article covers the step-to-step building process.