Module Resolution

Allow Synthetic Default Imports - allowSyntheticDefaultImports

When set to true, allowSyntheticDefaultImports allows you to write an import like:

  1. ts
    import React from "react";

instead of:

  1. ts
    import * as React from "react";

When the module does not explicitly specify a default export.

For example, without allowSyntheticDefaultImports as true:

  1. // @filename: utilFunctions.js
    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.
    const getStringLength = (str) => str.length;
  2. module.exports = {
    getStringLength,
    };
  3. // @filename: index.ts
    import utils from "./utilFunctions";
  4. const count = utils.getStringLength("Check JS");
    Try

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:

  1. js
    // @filename: utilFunctions.js
    const 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:

    esModuleInterop

  • Released:

    1.8

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:

    3.5

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.

  1. baseUrl
  2. ├── ex.ts
  3. ├── hello
  4. └── world.ts
  5. └── tsconfig.json

With "baseUrl": "./" inside this project TypeScript will look for files starting at the same folder as the tsconfig.json.

  1. 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 as const moment = require("moment")
  • a default import like import moment from "moment" acts the same as const 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:

  1. ts
    import * as fs from "fs";
    import _ from "lodash";
    fs.readFileSync("file.txt", "utf8");
    _.chunk(["a", "b", "c", "d"], 2);

With esModuleInterop disabled:

  1. "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);
    Try

With esModuleInterop set to true:

  1. "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);
    Try

Note: You can make JS emit terser by enabling importHelpers:

  1. "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);
    Try

Enabling esModuleInterop will also enable allowSyntheticDefaultImports.

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 or UMD or System or ES6, then Classic

    Otherwise Node

  • Related:

    module

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/imports.

  1. {
    "": ".", // 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.

  1. {
    "": "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. imports 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:

  1. src
  2. └── views
  3. └── view1.ts (can import "./template1", "./view2`)
  4. └── view2.ts (can import "./template1", "./view1`)
  5. generated
  6. └── templates
  7. └── views
  8. └── template1.ts (can import "./view1", "./view2")
  1. {
    "": ["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:

  1. sh
    src
    └── index.ts
    └── css
    └── main.css
    └── navigation.css
    generated
    └── css
    └── main.css.d.ts
    └── navigation.css.d.ts
  1. {
    "": ["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.

  1. // @filename: index.ts
    import { appClass } from "./main.css";
    Try
  • Released:

    2.0

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:

  1. {
    "": ["./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.

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:

  1. {
    "": ["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.

Module Resolution - 图1What 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:

  1. 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, or expect 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.