ES6 Modules
TypeScript 1.5 supports ECMAScript 6 (ES6) modules. ES6 modules are effectively TypeScript external modules with a new syntax: ES6 modules are separately loaded source files that possibly import other modules and provide a number of externally accessible exports. ES6 modules feature several new export and import declarations. It is recommended that TypeScript libraries and applications be updated to use the new syntax, but this is not a requirement. The new ES6 module syntax coexists with TypeScript’s original internal and external module constructs and the constructs can be mixed and matched at will.
Export Declarations
In addition to the existing TypeScript support for decorating declarations with export
, module members can also be exported using separate export declarations, optionally specifying different names for exports using as
clauses.
interface Stream { ... }
function writeToStream(stream: Stream, data: string) { ... }
export { Stream, writeToStream as write }; // writeToStream exported as write
Import declarations, as well, can optionally use as
clauses to specify different local names for the imports. For example:
import { read, write, standardOutput as stdout } from "./inout";
var s = read(stdout);
write(stdout, s);
As an alternative to individual imports, a namespace import can be used to import an entire module:
import * as io from "./inout";
var s = io.read(io.standardOutput);
io.write(io.standardOutput, s);
Re-exporting
Using from
clause a module can copy the exports of a given module to the current module without introducing local names.
export { read, write, standardOutput as stdout } from "./inout";
export *
can be used to re-export all exports of another module. This is useful for creating modules that aggregate the exports of several other modules.
export function transform(s: string): string { ... }
export * from "./mod1";
export * from "./mod2";
Default Export
An export default declaration specifies an expression that becomes the default export of a module:
export default class Greeter {
sayHello() {
console.log("Greetings!");
}
}
Which in turn can be imported using default imports:
import Greeter from "./greeter";
var g = new Greeter();
g.sayHello();
Bare Import
A “bare import” can be used to import a module only for its side-effects.
import "./polyfills";
For more information about module, please see the ES6 module support spec.
Destructuring in declarations and assignments
TypeScript 1.5 adds support to ES6 destructuring declarations and assignments.
Declarations
A destructuring declaration introduces one or more named variables and initializes them with values extracted from properties of an object or elements of an array.
For example, the following sample declares variables x
, y
, and z
, and initializes them to getSomeObject().x
, getSomeObject().y
and getSomeObject().z
respectively:
var { x, y, z } = getSomeObject();
Destructuring declarations also works for extracting values from arrays:
var [x, y, z = 10] = getSomeArray();
Similarly, destructuring can be used in function parameter declarations:
function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {
// Draw text
}
// Call drawText with an object literal
var item = { text: "someText", location: [1,2,3], style: "italics" };
drawText(item);
Assignments
Destructuring patterns can also be used in regular assignment expressions. For instance, swapping two variables can be written as a single destructuring assignment:
var x = 1;
var y = 2;
[x, y] = [y, x];
namespace
keyword
TypeScript used the module
keyword to define both “internal modules” and “external modules”;
this has been a bit of confusion for developers new to TypeScript.
“Internal modules” are closer to what most people would call a namespace; likewise, “external modules” in JS speak really just are modules now.
Note: Previous syntax defining internal modules are still supported.
Before:
module Math {
export function add(x, y) { ... }
}
After:
namespace Math {
export function add(x, y) { ... }
}
let
and const
support
ES6 let
and const
declarations are now supported when targeting ES3 and ES5.
Const
const MAX = 100;
++MAX; // Error: The operand of an increment or decrement
// operator cannot be a constant.
Block scoped
if (true) {
let a = 4;
// use a
}
else {
let a = "string";
// use a
}
alert(a); // Error: a is not defined in this scope.
for..of support
TypeScript 1.5 adds support to ES6 for..of loops on arrays for ES3/ES5 as well as full support for Iterator interfaces when targeting ES6.
Example
The TypeScript compiler will transpile for..of arrays to idiomatic ES3/ES5 JavaScript when targeting those versions:
for (var v of expr) { }
will be emitted as:
for (var _i = 0, _a = expr; _i < _a.length; _i++) {
var v = _a[_i];
}
Decorators
TypeScript decorators are based on the ES7 decorator proposal.
A decorator is:
- an expression
- that evaluates to a function
- that takes the target, name, and property descriptor as arguments
- and optionally returns a property descriptor to install on the target object
For more information, please see the Decorators proposal.
Example
Decorators readonly
and enumerable(false)
will be applied to the property method
before it is installed on class C
.
This allows the decorator to change the implementation, and in this case, augment the descriptor to be writable: false and enumerable: false.
class C {
@readonly
@enumerable(false)
method() { ... }
}
function readonly(target, key, descriptor) {
descriptor.writable = false;
}
function enumerable(value) {
return function (target, key, descriptor) {
descriptor.enumerable = value;
};
}
Computed properties
Initializing an object with dynamic properties can be a bit of a burden. Take the following example:
type NeighborMap = { [name: string]: Node };
type Node = { name: string; neighbors: NeighborMap; };
function makeNode(name: string, initialNeighbor: Node): Node {
var neighbors: NeighborMap = {};
neighbors[initialNeighbor.name] = initialNeighbor;
return { name: name, neighbors: neighbors };
}
Here we need to create a variable to hold on to the neighbor-map so that we can initialize it. With TypeScript 1.5, we can let the compiler do the heavy lifting:
function makeNode(name: string, initialNeighbor: Node): Node {
return {
name: name,
neighbors: {
[initialNeighbor.name]: initialNeighbor
}
};
}
Support for UMD
and System
module output
In addition to AMD
and CommonJS
module loaders, TypeScript now supports emitting modules UMD
(Universal Module Definition) and System
module formats.
Usage:
tsc —module umd
and
tsc —module system
Unicode codepoint escapes in strings
ES6 introduces escapes that allow users to represent a Unicode codepoint using just a single escape.
As an example, consider the need to escape a string that contains the character ’𠮷‘.
In UTF-16/UCS2, ’𠮷’ is represented as a surrogate pair, meaning that it’s encoded using a pair of 16-bit code units of values, specifically 0xD842
and 0xDFB7
.
Previously this meant that you’d have to escape the codepoint as "\uD842\uDFB7"
.
This has the major downside that it’s difficult to discern two independent characters from a surrogate pair.
With ES6’s codepoint escapes, you can cleanly represent that exact character in strings and template strings with a single escape: "\u{20bb7}"
.
TypeScript will emit the string in ES3/ES5 as "\uD842\uDFB7"
.
Tagged template strings in ES3/ES5
In TypeScript 1.4, we added support for template strings for all targets, and tagged templates for just ES6. Thanks to some considerable work done by @ivogabe, we bridged the gap for for tagged templates in ES3 and ES5.
When targeting ES3/ES5, the following code
function oddRawStrings(strs: TemplateStringsArray, n1, n2) {
return strs.raw.filter((raw, index) => index % 2 === 1);
}
oddRawStrings `Hello \n${123} \t ${456}\n world`
will be emitted as
function oddRawStrings(strs, n1, n2) {
return strs.raw.filter(function (raw, index) {
return index % 2 === 1;
});
}
(_a = ["Hello \n", " \t ", "\n world"], _a.raw = ["Hello \\n", " \\t ", "\\n world"], oddRawStrings(_a, 123, 456));
var _a;
AMD-dependency optional names
/// <amd-dependency path="x" />
informs the compiler about a non-TS module dependency that needs to be injected in the resulting module’s require call;
however, there was no way to consume this module in the TS code.
The new amd-dependency name
property allows passing an optional name for an amd-dependency:
/// <amd-dependency path="legacy/moduleA" name="moduleA"/>
declare var moduleA: MyType;
moduleA.callStuff();
Generated JS code:
define(["require", "exports", "legacy/moduleA"], function (require, exports, moduleA) {
moduleA.callStuff();
});
Project support through tsconfig.json
Adding a tsconfig.json
file in a directory indicates that the directory is the root of a TypeScript project.
The tsconfig.json file specifies the root files and the compiler options required to compile the project. A project is compiled in one of the following ways:
- By invoking tsc with no input files, in which case the compiler searches for the tsconfig.json file starting in the current directory and continuing up the parent directory chain.
- By invoking tsc with no input files and a -project (or just -p) command line option that specifies the path of a directory containing a tsconfig.json file.
Example
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"sourceMap": true
}
}
See the tsconfig.json wiki page for more details.
--rootDir
command line option
Option --outDir
duplicates the input hierarchy in the output.
The compiler computes the root of the input files as the longest common path of all input files;
and then uses that to replicate all its substructure in the output.
Sometimes this is not desirable, for instance inputs FolderA\FolderB\1.ts
and FolderA\FolderB\2.ts
would result in output structure mirroring FolderA\FolderB\
.
Now if a new file FolderA\3.ts
is added to the input, the output structure will pop out to mirror FolderA\
.
--rootDir
specifies the input directory to be mirrored in output instead of computing it.
--noEmitHelpers
command line option
The TypeSript compiler emits a few helpers like __extends
when needed.
The helpers are emitted in every file they are referenced in.
If you want to consolidate all helpers in one place, or override the default behavior, use --noEmitHelpers
to instructs the compiler not to emit them.
--newLine
command line option
By default the output new line character is \r\n
on Windows based systems and \n
on *nix based systems.
--newLine
command line flag allows overriding this behavior and specifying the new line character to be used in generated output files.
--inlineSourceMap
and inlineSources
command line options
--inlineSourceMap
causes source map files to be written inline in the generated .js
files instead of in a independent .js.map
file.
--inlineSources
allows for additionally inlining the source .ts
file into the .js
file.