WittCode💻

Create a TypeScript App

By

Learn how to create a TypeScript app, what TypeScript is, how it is different and similar to JavaScript, how to configure the TypeScript compiler with tsconfig.json, and how to install TypeScript.

Table of Contents 📖

What is TypeScript?

TypeScript is a programming language that adds static typing to JavaScript. This makes it easier to work with large JavaScript projects as errors are caught early. TypeScript also describes data, making it difficult to pass around unexpected data such as passing a number to a function that accepts a string, etc. Ultimately, TypeScript code (.ts files) is transpiled into JavaScript code (.js files) by a TypeScript compiler.

Creating an npm Project

To begin, create a folder to hold the project and navigate into it. Lets call the project create-a-typescript-app.

mkdir create-a-typescript-app
cd create-a-typescript-app

Now lets initialize the project as an ES6 npm project by using the command npm init es6 -y.

npm init es6 -y

This command turns our directory into an npm project by creating a package.json file. The -y flag adds default information about the project such as its name, version, and npm commands. Specifying ES6 allows us to ECMAScript import/export as opposed to CommonJS.

Installing TypeScript and Node Types

Now lets install TypeScript. TypeScript is simply an npm package called typescript. We need to install it as a development dependency with the -D flag as TypeScript is used for development but not production. This is because TypeScript simply points out errors early on. TypeScript code still needs to be converted to JavaScript before it is ran. To install typescript, run the command npm i typescript -D.

npm i typescript -D

To get the benefits of TypeScript, we also need to install npm packages from the @types scope. These packages contain TypeScript declaration files, or files with a .d.ts extension, that inform the TypeScript compiler about all the typing information for the specific library. For example, @types/express provides TypeScript type information for the express library and @types/node contains the types information for Node. In this project we will only be working with the core Node libraries so we will install @types/node as a development dependency with npm i @types/node -D.

npm i @types/node -D

If we wanted to use another library, such as express, we would install express with npm i express and then its typings with npm i @types/express -D.

Configuring TypeScript

Next, we need to create a tsconfig.json file. The tsconfig.json file indicates the root of a TypeScript project and is used to configure the TypeScript compiler. We can create a tsconfig.json file using npx and tsc. Npx is an npm package runner that allows us to run executable JavaScript packages without installing them. Tsc stands for The TypeScript Compiler and it allows us to work with the TypeScript compiler. We can create a tsconfig.json file by using the command npx tsc --init.

npx tsc --init

Checking the contents of tsconfig.json, we can see the default configuration for our TypeScript compiler. Most of the contents of the file are commented out, uncommenting that line will enable that functionality. Note that most of the contents of tsconfig.json are ommitted here as there are a lot of options.

{
    "compilerOptions": {
        /* Language and Environment */
        "target": "es2016",                 /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
        // "lib": [],                       /* Specify a set of bundled library declaration files that describe the target runtime environment. */
        // "jsx": "preserve",               /* Specify what JSX code is generated. */
        ...
    
        /* Modules */
        "module": "commonjs",               /* Specify what module code is generated. */
        // "rootDir": "./",                 /* Specify the root folder within your source files. */
        // "moduleResolution": "node10",    /* Specify how TypeScript looks up a file from a given module specifier. */
        // "baseUrl": "./",                 /* Specify the base directory to resolve non-relative module names. */
        ...

        /* Type Checking */
        "strict": true,                     /* Enable all strict type-checking options. */
        // "noImplicitAny": true,           /* Enable error reporting for expressions and declarations with an implied 'any' type. */
        // "strictNullChecks": true,        /* When type checking, take into account 'null' and 'undefined'. */
        // "strictFunctionTypes": true,     /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
        // "strictBindCallApply": true,     /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
        ...
    
        /* Completeness */
        // "skipDefaultLibCheck": true,     /* Skip type checking .d.ts files that are included with TypeScript. */
        "skipLibCheck": true                /* Skip type checking all .d.ts files. */
    }
}

Some examples of configuration options are setting outDir which specifies the output directory of the transpiled JavaScript files. For a demonstration, lets set our outDir to dist.

"outDir": "dist"

Now, when we transpile our TypeScript files they will be placed in a folder called dist. Therefore, we should update our package.json main key to point to the new index.js location.

"main": "./dist/index.js"

Lets also create a folder inside our TypeScript app called src.

mkdir src

Now lets use tsconfig.json to specify this folder as the location of our TypeScript files we want to compile. We can do this by using the top level key include.

"include": ["src/**/*"]

This tells the TypeScript compiler to compile every TypeScript file inside the src directory and its subdirectories. Now lets use tsconfig.json to specify the folder we want the TypeScript compiler to ignore. We want to ignore the node_modules folder and our output dist folder. We can ignore this folder using the top level exclude key.

"exclude": ["node_modules", "dist"]

Also, as we specified our project is using ECMAScript, we need to set the module key to NodeNext.

"module": "NodeNext"

The module key sets the module system for the transpiled JavaScript files. Setting it to NodeNext will allow us to transpile .mts files to .mjs files and .cts files to .cjs files. In other words, the transpiled files can either have ECMAScript import/export or CommonJS require/module.exports. We will be working with ECMAScript because we set the type key inside package.json to module when we ran npm init es6 -y.

Writing TypeScript Code

Now lets add some TypeScript code, create a file called index.ts inside the src directory and place the following code inside.

const sayHello = (name: string) => {
    console.log(`Hello ${name}!`);
};

sayHello('WittCode');

Now lets compile this TypeScript code into JavaSctipt by running the command npx tsc.

npx tsc

If we don't provide any arguments to npx tsc, then the TypeScript compiler searches for a tsconfig.json file. In other words, running npx tsc here will run the TypeScript compiler with the configuration we supplied inside tsconfig.json. We will now see the presence of a folder called dist with the file index.js inside it.

"use strict";
const sayHello = (name) => {
    console.log(`Hello ${name}!`);
};
sayHello('WittCode');

Creating Package.json Scripts

Finally, lets create some scripts inside our package.json file that simplify working with this project. First, lets add clean command that removes the dist folder.

"scripts": {
    "clean": "rm -rf dist"
},

Now lets add a build command that first runs clean, and then uses the npx tsc command to build a new dist folder.

"build": "npm run clean && npx tsc"

Now, running npm run build will build our project by converting our TypeScript code into JavaScript code using our tsconfig.json configuration. We should also add a command to run our project. To run this project, we will use the libraries nodemon and ts-node. Install them as development dependencies.

npm i nodemon ts-node -D

The ts-node library provides TypeScript execution and ECMAScript support. Nodemon is a library that automatically restarts Node applications when files are changed. We can use the exec option of nodemon to monitor other programs. We will use this functionality to have nodemon monitor our TypeScript application.

"start": "nodemon --exec node --loader ts-node/esm src/index.ts"

Here, we are running node with the ts-node/esm loader. This tells node to load modules with ts-node and ECMAScript and to register ts-node without using the CLI that ts-node provides. To run this command type npm start in the terminal. Any file changes will trigger a reload.

npm start

> start
> nodemon --exec node --loader ts-node/esm src/index.ts

[nodemon] 3.0.1
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: ts,json
[nodemon] starting `node --loader ts-node/esm src/index.ts`
(node:44756) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
Hello WittCode!
[nodemon] clean exit - waiting for changes before restart