@babel/plugin-transform-typescript

NOTE: This plugin is included in @babel/preset-typescript

This plugin adds support for the types syntax used by the TypeScript programming language. However, this plugin does not add the ability to type-check the JavaScript passed to it. For that, you will need to install and set up TypeScript.

Note that although the TypeScript compiler tsc actively supports certain JavaScript proposals such as optional chaining (?.), nullish coalescing (??) and class properties (this.#x), this preset does not include these features because they are not the types syntax available in TypeScript only. We recommend using preset-env with preset-typescript if you want to transpile these features.

Example

In

  1. const x: number = 0;

Out

  1. const x = 0;

Installation

  1. npm install --save-dev @babel/plugin-transform-typescript

Usage

  1. {
  2. "plugins": ["@babel/plugin-transform-typescript"]
  3. }

Via CLI

  1. babel --plugins @babel/plugin-transform-typescript script.js

Via Node API

  1. require("@babel/core").transformSync("code", {
  2. plugins: ["@babel/plugin-transform-typescript"],
  3. });

Options

allowDeclareFields

boolean, defaults to false

Added in v7.7.0

NOTE: This will be enabled by default in Babel 8

When enabled, type-only class fields are only removed if they are prefixed with the declare modifier:

  1. class A {
  2. declare foo: string; // Removed
  3. bar: string; // Initialized to undefined
  4. }

allowNamespaces

boolean, defaults to true.

History

VersionChanges
v7.5.0Added allowNamespaces, defaults to false
v7.13.0defaults to true

Enables compilation of TypeScript namespaces.

disallowAmbiguousJSXLike

boolean, defaults to false

Added in: v7.16.0

Even when JSX parsing is not enabled, this option disallows using syntax that would be ambiguous with JSX (<X> y type assertions and <X>() => {} type arguments). It matches the tsc behavior when parsing .mts and .mjs files.

isTSX

boolean, defaults to false

Forcibly enables jsx parsing. Otherwise angle brackets will be treated as TypeScript’s legacy type assertion var foo = <string>bar;. Also, isTSX: true requires allExtensions: true.

jsxPragma

string, defaults to React

Replace the function used when compiling JSX expressions. This is so that we know that the import is not a type import, and should not be removed.

jsxPragmaFrag

string, defaults to React.Fragment

Replace the function used when compiling JSX fragment expressions. This is so that we know that the import is not a type import, and should not be removed.

onlyRemoveTypeImports

boolean, defaults to false

Added in: v7.9.0

When set to true, the transform will only remove type-only imports (introduced in TypeScript 3.8). This should only be used if you are using TypeScript >= 3.8.

  1. class A {
  2. declare foo: string; // Removed
  3. bar: string; // Initialized to undefined
  4. prop?: string; // Initialized to undefined
  5. prop1!: string // Initialized to undefined
  6. }

optimizeConstEnums

boolean, defaults to false

Added in: v7.15.0

When set to true, Babel will inline enum values rather than using the usual enum output:

  1. // Input
  2. const enum Animals {
  3. Fish
  4. }
  5. console.log(Animals.Fish);
  6. // Default output
  7. var Animals;
  8. (function (Animals) {
  9. Animals[Animals["Fish"] = 0] = "Fish";
  10. })(Animals || (Animals = {}));
  11. console.log(Animals.Fish);
  12. // `optimizeConstEnums` output
  13. console.log(0);

This option differs from TypeScript’s --isolatedModules behavior, which ignores the const modifier and compiles them as normal enums, and aligns Babel’s behavior with TypeScript’s default behavior.

However, when exporting a const enum Babel will compile it to a plain object literal so that it doesn’t need to rely on cross-file analysis when compiling it:

  1. // Input
  2. export const enum Animals {
  3. Fish,
  4. }
  5. // `optimizeConstEnums` output
  6. export var Animals = {
  7. Fish: 0,
  8. };

TypeScript Compiler Options

The official TypeScript compiler has many options for configuring how it compiles and type checks. While many don’t apply, some behaviors might be useful and their equivalents in Babel can be enabled by some configuration options or plugins.

  • --alwaysStrict You can use the strictMode parser option:

    1. module.exports = {
    2. parserOpts: { strictMode: true },
    3. };
  • --downlevelIteration You can use the @babel/plugin-transform-for-of plugin. If you are using @babel/preset-env, for...of is already transpiled using iterators when it isn’t supported by your compilation target(s).

  • --emitDecoratorMetadata This option isn’t supported by an official Babel package since it is a TypeScript-specific addition and not part of the decorators proposal. If you rely on this feature, you can use the community plugin babel-plugin-transform-typescript-metadata.

  • --esModuleInterop This is the default behavior of Babel when transpiling ECMAScript modules.

  • --experimentalDecorators This option enables support for the “legacy” decorator proposal. You can enable it in Babel using the @babel/plugin-proposal-decorators plugin, but please be aware, there are some minor differences.

    1. module.exports = {
    2. plugins: [["@babel/plugin-proposal-decorators", { legacy: true }]],
    3. };
  • --importHelpers This is the equivalent of the @babel/plugin-transform-runtime package.

  • ---importsNotUsedAsValues You can use the onlyRemoveTypeImports option to replicate this behavior. onlyRemoveTypeImports: true is equivalent to importsNotUsedAsValues: preserve, while onlyRemoveTypeImports: false is equivalent to importsNotUsedAsValues: remove. There is no equivalent for importsNotUsedAsValues: error.

  • --inlineSourceMap You can set the sourceMaps: “inline” option in your babel.config.json file.

  • --isolatedModules This is the default Babel behavior, and it can’t be turned off because Babel doesn’t support cross-file analysis.

  • --jsx JSX support is provided using another plugin. If you want your output to contains JSX code (i.e. --jsx preserve), you need the @babel/plugin-syntax-jsx plugin; if you want to transpile it to standard JavaScript (i.e. --jsx react or --jsx react-native), you should use the @babel/plugin-transform-react-jsx plugin.

  • --jsxFactory It can be customized using the pragma option of the @babel/plugin-transform-react-jsx package. You also need to set the jsxPragma option of this plugin.

  • --module, -m If you are using a bundler (Webpack or Rollup), this option is set automatically. If you are using @babel/preset-env, you can use the modules option; otherwise you can load the specific plugin.

    —module value@babel/preset-env‘s modulesSingle plugin
    Nonefalse/
    CommonJS“commonjs” or “cjs”@babel/plugin-transform-modules-commonjs
    AMD“amd”@babel/plugin-transform-modules-amd
    System“systemjs”@babel/plugin-transform-modules-systemjs
    UMD“umd”@babel/plugin-transform-modules-umd
    ES6 or ES2015false/
  • --outDir When using @babel/cli, you can set the --out-dir option.

  • --outFile Babel doesn’t support concatenating output files: you should use a bundler (like Webpack, Rollup or Parcel) for that. When using @babel/cli, you can compile a single file using the --out-file option.

  • --sourceMap You can use the top-level sourceMaps: true option.

  • --target Babel doesn’t support targeting a specific version of the language, but you can choose which engines you want to target using @babel/preset-env. If you prefer, you can enable individual plugins for every ECMAScript feature.

  • --useDefineForClassFields You can use the onlyRemoveTypeImports option to replicate this behavior.

  • --watch, -w When using @babel/cli, you can specify the --watch option.

Caveats

Because there are features of the TypeScript language which rely on the full type-system to be available to make changes at runtime. This section of caveats is quite long, however, it’s worth noting that a few of these features are only found in older TypeScript codebases and have modern JavaScript equivalents which you are probably already using.

  1. Since Babel does not type-check, code which is syntactically correct, but would fail the TypeScript type-checking may successfully get transformed, and often in unexpected or invalid ways.

  2. This plugin does not support export = and import =, because those cannot be compiled to ES.next. These are a TypeScript only form of import/export.

    Workarounds:

  3. Changes to your tsconfig.json are not reflected in babel. The build process will always behave as though isolatedModules is turned on, there are Babel-native alternative ways to set a lot of the tsconfig.json options however.

  4. Q: Why doesn’t Babel allow export of a var or let?

    A: The TypeScript compiler dynamically changes how these variables are used depending on whether or not the value is mutated. Ultimately, this depends on a type-model and is outside the scope of Babel. A best-effort implementation would transform context-dependent usages of the variable to always use the Namespace.Value version instead of Value, in case it was mutated outside of the current file. Allowing var or let from Babel (as the transform is not-yet-written) is therefore is more likely than not to present itself as a bug when used as-if it was not const.

Impartial Namespace Support

If you have existing code which uses the TypeScript-only namespace features. Babel supports a subset of TypeScript’s namespace features. If you are considering writing new code which uses namespace, using the ES2015 import/export is recommended instead. It’s not going away, but there are modern alternatives.

  • Type-only namespaces should be marked with declare and will subsequently be safely removed.

  • exporting a variable using var or let in a namespace will result in an error: “Namespaces exporting non-const are not supported by Babel. Change to const or …”

    Workaround: Use const. If some form of mutation is required, explicitly use an object with internal mutability.

  • namespaces will not share their scope. In TypeScript, it is valid to refer to contextual items that a namespace extends without qualifying them, and the compiler will add the qualifier. In Babel, there is no type-model, and it is impossible to dynamically change references to match the established type of the parent object.

    Consider this code:

    1. namespace N {
    2. export const V = 1;
    3. }
    4. namespace N {
    5. export const W = V;
    6. }

    The TypeScript compiler compiles it to something like this:

    1. var N = {};
    2. (function(N) {
    3. N.V = 1;
    4. })(N);
    5. (function(N) {
    6. N.W = N.V;
    7. })(N);

    While Babel will transform it to something like this:

    1. var N;
    2. (function(_N) {
    3. const V = (_N = 1);
    4. })(N || (N = {}));
    5. (function(_N) {
    6. const W = V;
    7. })(N || (N = {}));

    As Babel doesn’t understand the type of N, the reference to V will be undefined resulting in an error.

    Workaround: Explicitly refer to values not in the same namespace definition, even if they would be in the scope according to TypeScript. Examples:

    1. namespace N {
    2. export const V = 1;
    3. }
    4. namespace N {
    5. export const W = N.V;
    6. }

    Or:

    1. namespace N {
    2. export const V = 1;
    3. export const W = V;
    4. }