- Module Resolution
- Allow Synthetic Default Imports -
allowSyntheticDefaultImports
- Allow Umd Global Access -
allowUmdGlobalAccess
- Base Url -
baseUrl
- ES Module Interop -
esModuleInterop
- Module Resolution -
moduleResolution
- Paths -
paths
- Preserve Symlinks -
preserveSymlinks
- Root Dirs -
rootDirs
- Type Roots -
typeRoots
- Types -
types
- What does this affect?
- Allow Synthetic Default Imports -
Module Resolution
Allow Synthetic Default Imports - allowSyntheticDefaultImports
When set to true, allowSyntheticDefaultImports
allows you to write an import like:
ts
import React from "react";
instead of:
ts
import * as React from "react";
When the module does not explicitly specify a default export.
For example, without allowSyntheticDefaultImports
as true:
Module '"/home/runner/work/TypeScript-Website/TypeScript-Website/packages/typescriptlang-org/utilFunctions"' has no default export.1192Module '"/home/runner/work/TypeScript-Website/TypeScript-Website/packages/typescriptlang-org/utilFunctions"' has no default export.// @filename: utilFunctions.js constgetStringLength = (str ) =>str .length ;
module .exports = {getStringLength ,};
// @filename: index.tsimportutils from "./utilFunctions";Try
constcount =utils .getStringLength ("Check JS");
This code raises an error because there isn’t a default
object which you can import. Even though it feels like it should. For convenience, transpilers like Babel will automatically create a default if one isn’t created. Making the module look a bit more like:
js
// @filename: utilFunctions.jsconst getStringLength = (str) => str.length;const allFunctions = {getStringLength,};module.exports = allFunctions;module.exports.default = allFunctions;
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.
Default:
module === “system” or esModuleInterop
Related:
Released:
Allow Umd Global Access - allowUmdGlobalAccess
When set to true, allowUmdGlobalAccess
lets you access UMD exports as globals from inside module files. A module file is a file that has imports and/or exports. Without this flag, using an export from a UMD module requires an import declaration.
An example use case for this flag would be a web project where you know the particular library (like jQuery or Lodash) will always be available at runtime, but you can’t access it with an import.
Default:
false
Released:
Base Url - baseUrl
Lets you set a base directory to resolve non-absolute module names.
You can define a root folder where you can do absolute file resolution. E.g.
baseUrl
├── ex.ts
├── hello
│ └── world.ts
└── tsconfig.json
With "baseUrl": "./"
inside this project TypeScript will look for files starting at the same folder as the tsconfig.json
.
ts
import { helloWorld } from "hello/world";console.log(helloWorld);
If you get tired of imports always looking like "../"
or "./"
. Or needing to change as you move files, this is a great way to fix that.
ES Module Interop - esModuleInterop
By default (with esModuleInterop
false or not set) TypeScript treats CommonJS/AMD/UMD modules similar to ES6 modules. In doing this, there are two parts in particular which turned out to be flawed assumptions:
- a namespace import like
import * as moment from "moment"
acts the same asconst moment = require("moment")
- a default import like
import moment from "moment"
acts the same asconst moment = require("moment").default
This mis-match causes these two issues:
- the ES6 modules spec states that a namespace import (
import * as x
) can only be an object, by having TypeScript treating it the same as= require("x")
then TypeScript allowed for the import to be treated as a function and be callable. This breaks the spec’s recommendations. - while accurate to the ES6 modules spec, most libraries with CommonJS/AMD/UMD modules didn’t conform as strictly as TypeScript’s implementation.
Turning on esModuleInterop
will fix both of these problems in the code transpiled by TypeScript. The first changes the behavior in the compiler,the second is fixed by two new helper functions which provide a shim to ensure compatibility in the emitted JavaScript:
ts
import * as fs from "fs";import _ from "lodash";fs.readFileSync("file.txt", "utf8");_.chunk(["a", "b", "c", "d"], 2);
With esModuleInterop
disabled:
Try
"use strict";Object.defineProperty(exports, "__esModule", { value: true });const fs = require("fs");const lodash_1 = require("lodash");fs.readFileSync("file.txt", "utf8");lodash_1.default.chunk(["a", "b", "c", "d"], 2);
With esModuleInterop
set to true
:
Try
"use strict";var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {if (k2 === undefined) k2 = k;Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });}) : (function(o, m, k, k2) {if (k2 === undefined) k2 = k;o[k2] = m[k];}));var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {Object.defineProperty(o, "default", { enumerable: true, value: v });}) : function(o, v) {o["default"] = v;});var __importStar = (this && this.__importStar) || function (mod) {if (mod && mod.__esModule) return mod;var result = {};if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);__setModuleDefault(result, mod);return result;};var __importDefault = (this && this.__importDefault) || function (mod) {return (mod && mod.__esModule) ? mod : { "default": mod };};Object.defineProperty(exports, "__esModule", { value: true });const fs = __importStar(require("fs"));const lodash_1 = __importDefault(require("lodash"));fs.readFileSync("file.txt", "utf8");lodash_1.default.chunk(["a", "b", "c", "d"], 2);
Note: You can make JS emit terser by enabling importHelpers
:
Try
"use strict";Object.defineProperty(exports, "__esModule", { value: true });const tslib_1 = require("tslib");const fs = tslib_1.__importStar(require("fs"));const lodash_1 = tslib_1.__importDefault(require("lodash"));fs.readFileSync("file.txt", "utf8");lodash_1.default.chunk(["a", "b", "c", "d"], 2);
Enabling esModuleInterop
will also enable allowSyntheticDefaultImports
.
Recommended:
True
Default:
false
Related:
Released:
Module Resolution - moduleResolution
Specify the module resolution strategy: 'node'
(Node.js) or 'classic'
(used in TypeScript before the release of 1.6). You probably won’t need to use classic
in modern code.
There is a handbook reference page on Module Resolution
Default:
module ===
AMD
orUMD
orSystem
orES6
, thenClassic
Otherwise
Node
Related:
Paths - paths
A series of entries which re-map imports to lookup locations relative to the baseUrl
, there is a larger coverage of paths
in the handbook.
paths
lets you declare how TypeScript should resolve an import in your require
/import
s.
{"": {"": ".", // this must be specified if "paths" is specified."": {"jquery": ["node_modules/jquery/dist/jquery"] // this mapping is relative to "baseUrl"}}}
This would allow you to be able to write import "jquery"
, and get all of the correct typing locally.
{"": {"": "src","": {"app/*": ["app/*"],"config/*": ["app/_config/*"],"environment/*": ["environments/*"],"shared/*": ["app/_shared/*"],"helpers/*": ["helpers/*"],"tests/*": ["tests/*"]},}
In this case, you can tell the TypeScript file resolver to support a number of custom prefixes to find code. This pattern can be used to avoid long relative paths within your codebase.
Preserve Symlinks - preserveSymlinks
This is to reflect the same flag in Node.js; which does not resolve the real path of symlinks.
This flag also exhibits the opposite behavior to Webpack’s resolve.symlinks
option (i.e. setting TypeScript’s preserveSymlinks
to true parallels setting Webpack’s resolve.symlinks
to false, and vice-versa).
With this enabled, references to modules and packages (e.g. import
s and /// <reference type="..." />
directives) are all resolved relative to the location of the symbolic link file, rather than relative to the path that the symbolic link resolves to.
Default:
false
Root Dirs - rootDirs
Using rootDirs
, you can inform the compiler that there are many “virtual” directories acting as a single root. This allows the compiler to resolve relative module imports within these “virtual” directories, as if they were merged in to one directory.
For example:
src
└── views
└── view1.ts (can import "./template1", "./view2`)
└── view2.ts (can import "./template1", "./view1`)
generated
└── templates
└── views
└── template1.ts (can import "./view1", "./view2")
{"": {"": ["src/views", "generated/templates/views"]}}
This does not affect how TypeScript emits JavaScript, it only emulates the assumption that they will be able to work via those relative paths at runtime.
rootDirs
can be used to provide a separate “type layer” to files that are not TypeScript or JavaScript by providing a home for generated .d.ts
files in another folder. This is technique is useful for bundled applications where you use import
of files that aren’t necessarily code:
sh
src└── index.ts└── css└── main.css└── navigation.cssgenerated└── css└── main.css.d.ts└── navigation.css.d.ts
{"": {"": ["src", "generated"]}}
This technique lets you generate types ahead of time for the non-code source files. Imports then work naturally based off the source file’s location. For example ./src/index.ts
can import the file ./src/css/main.css
and TypeScript will be aware of the bundler’s behavior for that filetype via the corresponding generated declaration file.
Try
// @filename: index.tsimport {appClass } from "./main.css";
Released:
Type Roots - typeRoots
By default all visible ”@types
” packages are included in your compilation. Packages in node_modules/@types
of any enclosing folder are considered visible. For example, that means packages within ./node_modules/@types/
, ../node_modules/@types/
, ../../node_modules/@types/
, and so on.
If typeRoots
is specified, only packages under typeRoots
will be included. For example:
{"": {"": ["./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
.
Related:
Types - types
By default all visible ”@types
” packages are included in your compilation. Packages in node_modules/@types
of any enclosing folder are considered visible. For example, that means packages within ./node_modules/@types/
, ../node_modules/@types/
, ../../node_modules/@types/
, and so on.
If types
is specified, only packages listed will be included in the global scope. For instance:
{"": {"": ["node", "jest", "express"]}}
This tsconfig.json
file will only include ./node_modules/@types/node
, ./node_modules/@types/jest
and ./node_modules/@types/express
. Other packages under node_modules/@types/*
will not be included.
What does this affect?
This option does not affect how @types/*
are included in your application code, for example if you had the above compilerOptions
example with code like:
ts
import * as moment from "moment";moment().format("MMMM Do YYYY, h:mm:ss a");
The moment
import would be fully typed.
When you have this option set, by not including a module in the types
array it:
- Will not add globals to your project (e.g
process
in node, orexpect
in Jest) - Will not have exports appear as auto-import recommendations
This feature differs from typeRoots
in that it is about specifying only the exact types you want included, whereas typeRoots
supports saying you want particular folders.
Related: