TypeScript Compile Options

E.Y.
5 min readJun 30, 2020
Photo by Monika Grabkowska on Unsplash

In the past few days, when I started to dig into ambient modules, I also come across the concept of TypeScript configuration. Obviously it’s not a new concept, but I’m still far from being mastering it. So I think it will be worth to have a separate blog discussing the popular configuration options — in tsconfig.json .

I put here 3 examples of tsconfig, and will go through the properties in each of them:

//example.1
{
“compilerOptions”: {
“declaration”: true,
“declarationDir”: “types”,
“esModuleInterop”: true,
“jsx”: “react”,
“module”: “es6”,
“target”: “es6”,
“baseUrl”: “.”
},
“include”: [“src/types/**/*.ts”]
}
//example.2{
“compilerOptions”: {
“allowSyntheticDefaultImports”: true,
“declaration”: true,
“declarationDir”: “types”,
“esModuleInterop”: true,
“jsx”: “react”,
“module”: “commonjs”,
“moduleResolution”: “node”,
“strict”: true,
“target”: “es5”,
“lib”: [“es5”, “es6”, “es7”, “es2017”, “dom”],
“baseUrl”: “.”,
“paths”: {
“@myPackage/internal-components”: [“src”]
}
},
“include”: [“src/**/*”],
“exclude”: [“**/*.spec.js”, “**/*.spec.ts”, “**/*.spec.tsx”, “**/*.stories.tsx” ]
}
//example.3{
“compilerOptions”: {
“esModuleInterop”: true,
“jsx”: “preserve”,
“module”: “esnext”,
},
“include”: [
“src/**/*.ts”,
“src/**/*.tsx”
],
“exclude”: [
“**/*.spec.ts”,
“**/*.spec.tsx”,
],
“files”:
“src/ambient/window.d.ts”
]
}

Files

By default, TypeScript compiles recursively searches for all files in the root directory. But this is not encouraged, since without constraint, when another project references yours, it would have to read and parse every file in the compilation to figure out where to find the .d.ts file for any particular input. It would also have to do this to see if the project was up-to-date or not

However, we can explicitly define where TS search for files, and this is throughfiles option.

“files”: [
“src/ambient/window.d.ts”
]

Note if window.d.ts has a top level import then the imported file will be compiled as well.

include & exlude

Instead of listing each file manually, we can use include option to specify directories with file patterns. For example, we can include every .ts file in folder src/types

“include”: [“src/types/**/*.ts”]

And we can do the same to exclude any file following a pattern using exclude keyword as well.

Note that the priority for the 3 options above are Files> exclude > include , meaning that the files listed in files will be compiled even it’s excluded in exclude option. And if it’s both listed in exclude and include then it will be excluded.

Note that the pattern **/* simply means all folder and any files with extensions .ts/.tsx will be included.

target

When TS compiles into JavaScript, we want to make sure the version of JavaScript we get. So we can use target option.

“target”: “es6”

module

If we want to transpile ES6 modules into a different module system: CommonJS, AMD, etc, we can use module option. By default the module transpiles to ES6 if target is ES6, or CommonJS otherwise.

“module”: “commonjs”

lib

To be able to use classes from the ES standard libraries in your TS sources, you should use lib option and specify all standard ES libraries interfaces used in your sources. Note that some libraries are included by default likeDOM,ES6,DOM.Iterable,ScriptHost for ES6 , but when you specify lib option you overwrite it, and need to manually define it again.

“lib”: [“es5”, “es6”, “es7”, “es2017”, “dom”]

moduleResolution

In TS, modules are resolved differently based on whether the module reference is relative or non-relative. It will be set to node if module:CommonJS and classic by default.

“moduleResolution”: “node”,

With node, if you have a relative import like import moduleA from “./moduleA” in source file /src/myComponent.ts, TS will look up declaration in paths like:

 /src/moduleA.ts
/src/moduleA.tsx
/src/moduleA.d.ts
/src/moduleA/package.json
/src/moduleA/index.ts
/src/moduleA/index.tsx
/src/moduleA/index.d.ts

With a non-relative import like import moduleA from “moduleA” in source file /src/myComponent.ts, TS will look up paths like below in /src/node_modules and /node_modules

/src/node_modules/moduleA.ts
/src/node_modules/moduleA.tsx
/src/node_modules/moduleA.d.ts
/src/node_modules/moduleA/package.json
/src/node_modules/@types/moduleA.d.ts
/src/node_modules/moduleA/index.ts
/src/node_modules/moduleA/index.tsx
/src/node_modules/moduleA/index.d.tsx.
/src/node_modules/@types/moduleA.d.ts

When Classic it’s much simpler. With relative import, TS will look for

/pathToModule/moduleA.ts
/pathToModule/moduleA.d.ts

With non-relative path import in a file /src/components/app.ts, TS will look up files in paths:

/src/components/moduleA.ts
/src/components/moduleA.d.ts
/components/moduleA.ts
/components/moduleA.d.ts
/src/moduleA.ts
/src/moduleA.d.ts
/moduleA.ts
/moduleA.d.ts

baseUrl & paths

Note that when options is set to node , TS looks up node_modules folder for non relative module import. But it can happen that your imported module is in another folder, so you will need the flexibility to tell TS to look for other routes:

baseUrl”: “.”,
“paths”: {
“dateJS”: [“src”]

This is to tell TS for module @myPackage/internal-components in src folder instead of node_modules folder. TS will look for dateJS.ts or dateJS.d.ts in src first and if not found, will look inside src/dateJS directory. It will first try to locate package.json file with typings property specifying the main file, and if not found will default to src/dateJS/index.ts or src/dateJS/index.d.ts .

In order to incliude any modules not only dateJS, using asterisk wildcard like below and when you reference modules do something like “ import ModuleA from src/ModuleA

"baseUrl": ".",
"paths": {
"*": [
"src/*" //* maps any files under src.
]
}

declaration & declarationDir

There are time when we need to create ambient files to add declaration for a 3rd party libraries or any imported assets without declarations. You will most likely need to generate and consume declaration files yourself. But sometimes especially when you want to publish your NPM package, you should include the .js and the .d.ts files - and not the .ts files. This makes your package really portable, and ensures it can be used by both TypeScript projects and plain old JavaScript projects. This is when declaration comes handy

  "declaration": true

What this do is to instruct the TS compiler to output declaration files (.d.ts).

Sometimes it is convenient to output declaration files into one place (the types folder )using declarationDir option and put into one file together:

{
"declaration": true,
"declarationDir": "types"
"outFile": "types/index.d.ts"
}
}

typeRoots

When typeRoots is specified, TypeScript will only look for declaration files defined in the path of typeRoots.

{
"typeRoots": [
"./node_modules/@types",//include this otherwise will
//overwrite default behaviour
"./types"
]
}

allowSyntheticDefaultImports

When allowSyntheticDefaultImports is set to true, you no longer need to include * in import.

So instead of import * as moduelA from "moduleA" , you can write import moduleA from "moduleA" .

esModuleInterop

When esModuleInterop is set to true , you don’t need to use the old require keyword to import libraries .

There is a lot more to explore other than what is listed above, when I put more focus on compilation, but checkout for full list of configuration here in the documentation.

Happy reading!

--

--