Typescript build tools overview

Published on

TSC

TSC is the typescript command line tool.

By default it will use tsconfig.json for configuration.

This command line tool will compile typescript into typescript.

If you have a file called test.ts, and you run tsc test.ts, it will compile it into testy123.js.

You can configure it with a tsconfig.json. It'll automatically use any in the directory (and will go up to all parent directories), but you can also define where that config should be with `tsc -p ./config/tsconfig.json'.

tsconfig.json

This tsconfig.json supports JSON5 https://json5.org/ - which means you can add comments and trailing commas are ok.

The configuration of tsconfig.json has two parts - root options and compiler options.

root level options in tsconfig.json

Root level config options are for things such as what files to compile (with files, exclude, include).

You can also use extends to define what configurations it is extending.

Example:

tsconfig.json
{
  "extends": "./tsconfig-base-config.json",
  "compilerOptions": {
  }, // See section below
  "files": [
     "src/main.ts"
  ]
}

compiler options

Compiler options are the ones I tend to end up customising more often.

There are lots of compiler options. Some are booleans such as allowUnreachableCode. Some take strings (such as baseUrl).

One really easy to use resource is https://www.typescriptlang.org/tsconfig

Some important configs:

allowJs and checkJs

allowJs allows JavaScript files to be imported inside your project, instead of just .ts and .tsx files.

When used with checkJs you can get TypeScript to check for errors in your .js files. This is the equivalent of including // @ts-check at the top of all JavaScript files which are included in your project.

baseUrl

Set base directory to resolve non-absolute module names.

This is useful to get rid of the ./ or ../ (parent dir) in imports.

Set this (e.g. to "baseUrl": "./src") so you can do imports such as import SomeComponent from 'components/something' and it'll map to ./src/components/something.ts.

module

Module resolution is the process the compiler uses to figure out what an import refers to.

The module setting in tsconfig.json's compiler options tell tsc how to find imported modules.

This is often more important once you start importing non relative path imports.

There are two main modes - classic and node.

You can also set it to amd, system, umd, es2015, esnext. esnext seems to be the most common module resolution in most recent apps I've seen.

paths

The paths config like it belongs in the root options (not compiler options) to go with files, exclude, include etc. But, it belongs in the compiler options.

You use it to tell TypeScript how it should resolve an import in your require/imports.

example usage:

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".", // this must be specified if "paths" is specified.
    "paths": {
      "yourImport": ["node_modules/something/dist/yourImport"] // this mapping is relative to "baseUrl"
    }
  }
}

Then in your typescript files, you could import node_modules/something/dist/yourImport with:

index.ts
import yourImport from "yourImport"

A more typical use might look like this:

tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "src",
    "paths": {
        "app/*": ["app/*"],
        "config/*": ["app/_config/*"],
        "environment/*": ["environments/*"],
        "shared/*": ["app/_shared/*"],
        "helpers/*": ["helpers/*"],
        "tests/*": ["tests/*"]
    },
}
rootDirs

If you have a couple of directories and want to import files between them, you can use rootDirs.

Example file structure:

 src
 └── views
     └── view1.ts (can import "./template1", "./view2`)
     └── view2.ts (can import "./template1", "./view1`)

 generated
 └── templates
         └── views
             └── template1.ts (can import "./view1", "./view2")
tsconfig.json
{
  "compilerOptions": {
    "rootDirs": ["src/views", "generated/templates/views"]
  }
}
typeRoots

By default tsc will look for all type definitions in node_modules/@types. But if you want to define them somewhere else, you can use typeRoots.

Example usage:

tsconfig.json
{
  "compilerOptions": {
    "typeRoots": ["./typings", "./vendor/types"]
  }
}

This config file will include all packages under ./typings and ./vendor/types, and no packages from ./node_modules/@types. All paths are relative to the tsconfig.json.

You can also use types to define what packages from the type roots (or the default one) are included.

declaration

Set "declaration": true, in your tsconfig file to auto generate the typing files (.d.ts). You can use it with emitDeclarationOnly.

allowSyntheticDefaultImports

Set allowSyntheticDefaultImports to true so instead of importing import * as React from "react", you can instead import like import React from "react".

This is a nicer way to do imports in my opinion.

This flag does not affect the JavaScript emitted by TypeScript, it only for the type checking. This option brings the behavior of TypeScript in-line with Babel, where extra code is emitted to make using a default export of a module more ergonomic.

forceConsistentCasingInFileNames

This is a very important setting if you have a range of developers on different machines - some where case sensitivity matters on filenames and paths.

If a file attempts to import fileManager.ts by specifying ./FileManager.ts the file will be found in a case-insensitive file system, but not on a case-sensitive file system.

Turning this on will cause typescript to show an error if the incorrect file case is used in an import.

# Run a compile based on a backwards look through the fs for a tsconfig.json
tsc

# Emit JS for just the index.ts with the compiler defaults
tsc index.ts

# Emit JS for any .ts files in the folder src, with the default settings
tsc src/*.ts

# Emit files referenced in with the compiler settings from tsconfig.production.json
tsc --project tsconfig.production.json

# Emit d.ts files for a js file with showing compiler options which are booleans
tsc index.js --declaration --emitDeclarationOnly

# Emit a single .js file from two files via compiler options which take string arguments
tsc app.ts util.ts --target esnext --outfile index.js