Create Web Development Environments Using TypeScript, Webpack, with/without React

webpack ts react

A Complete Guide to Using TypeScript in Web Development with React, Create React App, and Next.js.

webpack ts react

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

Run β€˜tscβ€Šβ€”β€Šhelp’ from the terminal to see a list of tsc commands.

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 --noEmittype 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
Initializes a TypeScript project by running β€˜npx tscβ€” init’. Screenshot by author.

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 --noEmittypechecks 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. The tsconfig.json file specifies the root files and the compiler options required to compile the TypeScript-based projects.

β€” TypeScript Documentation: What Is tsconfig.json

Run command:

$ tsc --init

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.json is generated by β€˜tsc β€” init’. Screenshot by author.

A TSConfig Base example:

Source: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

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 or false
  • number – 12 or 12.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 devspins 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-loaderis 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

Left: ./dist/index.html; right: ./src/main.ts Screenshot by Amy Li
./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.
Demo the web dev environment built with TypeScript and the Webpack Dev Server. GIF by Amy Li

πŸ”— 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:

(Left) Run webpack dev server. (Right) Open the web page from a browser on port 9000. Screenshot by Amy Li

πŸ”— 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 docsNext.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-domnext , eslinteslint-config-nexttypescript@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:

(Left) Run next dev. (Right) Open the Next.js app from a browser on port 3001. Screenshot by Amy Li

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

Scroll to Top