Setting up a TypeScript Project


This is a very small tutorial on how to setup a TypeScript project:

  • How to create a project
  • How to compile
  • Explore compiler options
  • Using a tsconfig.json file
  • Using typescript libraries
  • Using JavaScript libraries with TypeScript Declaration files

For this tutorial you will need to have the following installed

  • Node (download from here)
  • Yarn (from here or install using npm)

Setup (very first steps)

Open a terminal and create a new directory to work on our demo application.

$ mkdir demo && cd demo

Let's first initialise a new yarn project and using default values (the -y option does not prompt for questions):

$ yarn init -y

You should now have a package.json file inside your directory. Next you can add a dependency to typescript (let's fix the version of typescript)

$ yarn add typescript@3.1.6 --dev

We have not installed typescript globally, only local for this project. This makes it easier to manage your typescript versions.

let's create our first typescript (cough) file named src/hello.ts

function say(what){

var txt = "hello friend";


You are now ready to compile (you need to use yarn as you are running tsc locally from the node_modules directory and not from a globally installed typescript compiler.)

$ yarn tsc src/hello.ts

Yes!, you have compiled your first typescript file (cough). You should have a transpiled javascript file src/hello.js

You can run this file using node:

$ node src/hello.js

Voila it should print "hello friend".

Well too be honest you have not really used any of the typescript syntactic sugar. Typescript is a superset of ECMAScript, and that's what you used. If your would do a diff on the original and transpiled file you would notice there are no differences:

$ diff src/hello.ts src/hello.js

Using the watch option

Just before we continue : you can also pass a watch option that scans your input files for changes and compiled automatically upon a change:

$ yarn tsc --watch src/hello.ts

Now when changing your source, it will automatically recompile it.

Using some real TypeScript

Let's spice things up a bit and use some of the syntactic sugar provided by typescript:

Add a type annotation to the what parameter of say: function say(what: string)

Transpile and run your file again. The result should be the same. Do notice the generated javascript file is now different from your typescript one.

$ diff src/hello.ts src/hello.js

Now change the value of the txt variable to 12 (as in the number 12): var txt = 12;.

When you try compile your file now, you should get the following error

src/hello.ts(5,5): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

Change it back to a string to make it compilable again. Next we explore using different compiler options.

Compiler Options

Let's list the command line options of the compiler:

$ yarn tsc  -h


Notice the default value for the --target is es3. Let's see this in action.

Change your txt variable declaration to a constant: const txt = "hello friend";, and recompile:

$ yarn tsc src/hello.ts

Notice the transpiled javascript uses var. This is because const was introduced in es6. Let's compile with this target and see what happens:

$ yarn tsc -t es6 src/hello.ts

You will now find the compiler has transpiled your statement to use const


We will explore another option. By default null and undefined are bottom values. This means every variable can be assigned their values (so in other words the value space of txt include strings as well as null and undefined.). You might want to read-up on Null References: The Billion Dollar Mistake.

Since typescript 2.0 we can exclude null and undefined from the value-space of other types. This way you accidentally place a null value where you were not expecting it.

To illustrate, add a function shout:

function shout(what : string){
    const loud = what.toUpperCase();

Call the function once with a value, and once with null (which is allowed!):

shout("shout, let it all out")

You should see your first message, and then the infamous error message: "cannot read property … of null".

Let's now compile it with the strictNullChecks option:

$ yarn tsc -t es6  --strictNullChecks  src/hello.ts

Observe the error you received:

src/hello.ts(13,7): error TS2345: Argument of type 'null' is not assignable to parameter of type 'string'.

If you would still like to allow people to pass null (blech), you could declare the what parameter as a union type: (what: string | null)

When you compile, you'll notice passing null (blech) is syntactically correct, however there's a problem now when you access the what parameter as it might be null (the compiler will actually prevent this with an error "Object is possibly 'null'.")

What should the function's semantics be in case null is passed? Perhaps we don't shout anything:

function shout(what : string | null){
    if (what){
        const loud = what.toUpperCase();

This is now a rather silly function: we are allowing null to be passed, but then we don't do anything. The option where we did not allow for null seems a better solution. But leave if for now.

You have used two compiler options and every time you call the compiler, you must pass these arguments. In general it is better to create a tsconfig.json file instead. You'll be exploring that next.

Using tsconfig.json file

A tsconfig.json contains the configuration for your typescript project. You can have tsc generate one for you, by passing it the --init option:

$ yarn tsc --init

Compiler options

When you open the file, you'll notice it gives you a head-start plus a whole bunch of options commented out. Feel free to explore this list. Eventually you should only include the following:

  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "inlineSources": true,
    "strictNullChecks": true

You can now run the compiler without passing any command line arguments:

$ yarn tsc

Include/Exclude (fyi)

By default tsc picks up all typescript files (.ts, .d.ts and .tsx) files in the containing directory and its subdirectories except those excluded by default (such as node_modules and bower_components, see handbook for exact defaults)

You can use the following options:

  • files: an array of filenames to include
  • include: an array of glob-like patterns
  • exclude: an array of glob-like patterns to exclude files from include (has no effect on explicitly listed files)

We won't have to do anything as the default works for us.

Using a library (TypeScript)

Let's use a third party library. We will choose rxjs for this example.

First we add the dependency:

$ yarn add rxjs@6.3.3

Then let's add the following code in a new file named src/rxjs-sample.ts

import { interval } from 'rxjs';
import {map} from 'rxjs/operators';

const source = interval("every second");

Try to compile this. You should see an error that the string is not assignable to a parameter of type 'number | undefined'

How does the typscript compiler know the type annotations for the rxjs methods? That's because rxjs is also build in typescript. Let's try another library

Using a non typescript library

What about node iteself? Create a new file named src/node-sample.ts and paste the following code in it:

import * as fs from 'fs';

fs.readdir(true, (err, items) => {

When you try and compile this code, you should see an error "Cannot find module 'fs'. ". Also the first parameter for the readdir function should be a path (see docs.

We need to include the TypeScript Declaration files for node. These are d.ts files that provide typescript type information about an library that's written in javascript. We will just add the ones for node to our project and see what happens:

$ yarn add @types/node --dev

When you now try to compile again, you should see the following error: "Argument of type 'true' is not assignable to parameter of type 'PathLike'". Great so now the typescript compiler has all the information about types.