WittCode💻

Create a TypeScript App with Webpack

By

Learn how to create a TypeScript app using Webpack. We will also go over some useful tsconfig.json options and how to use them.

Table of Contents 📖

Project Setup and Library Installation

To begin, lets initialize this project as an ES6 npm project using npm init es6 -y.

npm init es6 -y

Next, lets install webpack, webpack-cli, and typescript as development dependencies from npm.

npm i webpack webpack-cli typescript -D

Now, for Webpack to handle TypeScript code, we need to install a TypeScript loader. A webpack loader is a function that transforms source code. A TypeScript loader transforms TypeScript to JavaScript.

npm i ts-loader -D

The ts-loader uses tsc (the TypeScript compiler) and relies on a tsconfig.json file. Next lets install a Webpack plugin called the HTML Webpack plugin.

npm i html-webpack-plugin -D

A plugin is a feature of webpack that allows us to access, or plug in to, webpack's lifecycle. The HTML Webpack plugin allows us to create an HTML file to serve the bundle we create.

Configuring TypeScript with tsconfig.json

Now lets create a TypeScript configuration file, tsconfig.json, to configure the TypeScript compiler. We can do this by running npx tsc --init.

npx tsc --init

This creates a tsconfig.json file and fills it with some default values.

  • target - Specifies the JavaScript version the code is transpiled into. We will set it to ES6 as modern browsers support all ES6 features.
  • module - Sets the module system for the application. As we initialized this as an ES6 project, we will set this to ES6.
  • strict - Enables stricter TypeScript type checking behavior.
  • esModuleInterop - Assists with importing CommonJS modules into an ES6 codebase.
  • skipLibCheck - Skips the type checking of declaration files.
  • forceConsistentCasingInFileNames -TypeScript will throw an error if a program imports a file with different casing than in the file system.

We should also set the output directory of the compiled JavaScript files by using the outDir key.

"outDir": "dist"

Now all our JavaScript files will be emitted into a folder called dist. Another useful option is setting sourceMap to true.

"sourceMap": true

Setting sourceMap to true will make TypeScript emit source maps for each emitted JavaScript file. Source maps are useful for debugging as they allow browsers to reconstruct the original code. All of these options are under the compilerOptions object in tsconfig.json. Lets also set the top level options include and exclude.

"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]

The include key specifies an array of filenames or patters to include in the program. The exclude key specifies an array of filenames or patters that should be skipped when resolving include. We want to exclude any node_modules and dist folders.

Creating a Webpack Configuration File

Now lets configure Webpack to handle TypeScript code.

const path = require('path');

module.exports = {
  mode: 'development',
  target: 'web',
  entry: './src/index.ts',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    clean: true,
  }
};

Here we tell Webpack to bundle the application for development in a browser environment, that the entry point to start the bundling process is src/index.ts, and that we want to output our bundled code as a file called bundle.js inside a folder called dist. Now lets inform webpack that for every file ending in the .ts extension, we should run it through the ts-loader.

module: {
  rules: [
    {
      test: /\.ts$/,
      use: 'ts-loader',
      exclude: /node_modules/
    }
  ]
}

Remember, the ts-loader will look at the configuration inside tsconfig.json and use tsc (the TypeScript compiler) to compile the TypeScript code. Inside our tsconfig.json file we enabled source maps. To get Webpack to include these source maps in the bundle, we need to set the devtool option.

devtool: 'inline-source-map'

The devtool option configures how source maps are generated. Another useful Webpack configuration is to tell Webpack what extensions to resolve.

resolve: {
  extensions: ['.ts', '.js'],
},

This option alters how Webpack resolves modules, specifically it allows us to leave off extensions when importing files. As we will be working with TypeScript files, it will be useful not having to specify that throughout. Finally, lets inform Webpack about our HTML Webpack plugin. This can be done with the plugins key.

const HtmlWebpackPlugin = require('html-webpack-plugin');
...
...
plugins: [
  new HtmlWebpackPlugin()
]

Adding a Server to Serve up the Application

Lets now set up a server to serve up the application. For this we will use the Webpack dev server. Install it from npm as a development dependency.

npm i webpack-dev-server -D

Lets configure the Webpack dev server to serve up files on port 4444.

devServer: {
  static: path.resolve(__dirname, 'dist'),
  port: 4444
}

The static key is the path to serve static content from and port is the port to listen on. We want to serve the static content present in our dist folder.

Creating the TypeScript Code

Now lets have our TypeScript application create an tag with the WittCepter (my chrome extension! Go check it out!) logo whenever a button is clicked.

/**
 * Create an <img> tag with the src set to the
 * WittCode logo.
 */
export default (): void => {
  const myImg: HTMLImageElement = document.createElement('img');
  myImg.src = 'https://lh3.googleusercontent.com/lYi_qPYP6ayDgmT_Z5Ovzm1XQmtpMXy4hzRtANyBpAZVcUlaGNgCUSUAcOVh4C4T4RlB1Sen-WFb8nw43ytH4p3vPw=s60';
  document.body.appendChild(myImg);
};
import createWittCode from './wittcepter';

// Add button
const myBtn: HTMLButtonElement = document.createElement('button');
myBtn.innerText = 'Add WittCepter';
myBtn.onclick = createWittCode;
document.body.appendChild(myBtn);

Now whenever we click the "Add WittCepter" button we will see a new image appear. A very useful application!

Image

Running the Application

To run this application lets create a simple npm start script.

"start": "webpack serve --open --config webpack.config.cjs",

Running npm start will now open a browser window and serve up the static files on port 4444.

npm start

> start
> webpack serve --open

<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://localhost:4444/
<i> [webpack-dev-server] On Your Network (IPv4): http://192.168.0.23:4444/
<i> [webpack-dev-server] On Your Network (IPv6): http://[fe80::1]:4444/
<i> [webpack-dev-server] Content not from webpack is served from '/Users/wittcode/Desktop/create-a-typescript-app-with-webpack/dist' directory
<i> [webpack-dev-middleware] wait until bundle finished: /
asset bundle.js 621 KiB [emitted] (name: main)
asset index.html 233 bytes [emitted]
runtime modules 27.2 KiB 12 modules

Note that after running this command we don't see the dist folder present in the folder structure. This is because the webpack-dev-server holds it in memory. Lets also create a build script to output the application.

"build": "webpack --config webpack.config.cjs"

Running this command will output the application to the dist folder.