TypeScript 1.5
ES6 模块
TypeScript 1.5 支持 ECMAScript 6(ES6)模块。 ES6 模块实际上是具有新语法的 TypeScript 外部模块:ES6 模块是单独加载的源文件,可能导入其他模块并提供多个外部可访问的导出。 ES6 模块具有几种新的导出和导入声明。 建议 TypeScript 库和应用程序更新以使用新语法,但这不是强制要求。 新的 ES6 模块语法与 TypeScript 原有的内部模块和外部模块结构共存,并且这些结构可以任意混合和匹配。
导出声明
除了现有的使用 export 装饰声明的 TypeScript 支持外,模块成员还可以使用单独的导出声明导出,并且可选地使用 as 子句为导出指定不同的名称。
interface Stream { ... }
function writeToStream(stream: Stream, data: string) { ... }
export { Stream, writeToStream as write }; // writeToStream 作为 write 导出导入声明也可以选择使用 as 子句为导入指定不同的本地名称。例如:
import { read, write, standardOutput as stdout } from "./inout";
var s = read(stdout);
write(stdout, s);作为单独导入的替代,可以使用命名空间导入来导入整个模块:
import * as io from "./inout";
var s = io.read(io.standardOutput);
io.write(io.standardOutput, s);重新导出
使用 from 子句,模块可以将给定模块的导出复制到当前模块,而无需引入本地名称。
export { read, write, standardOutput as stdout } from "./inout";export * 可用于重新导出另一个模块的所有导出。这对于创建聚合其他几个模块导出的模块非常有用。
export function transform(s: string): string { ... }
export * from "./mod1";
export * from "./mod2";默认导出
默认导出声明指定一个表达式,该表达式成为模块的默认导出:
export default class Greeter {
sayHello() {
console.log("Greetings!");
}
}然后可以使用默认导入导入:
import Greeter from "./greeter";
var g = new Greeter();
g.sayHello();裸导入
“裸导入”可以仅为了其副作用而导入一个模块。
import "./polyfills";有关模块的更多信息,请参阅 ES6 模块支持规范。
声明和赋值中的解构
TypeScript 1.5 增加了对 ES6 解构声明和赋值的支持。
声明
解构声明引入一个或多个命名变量,并用从对象属性或数组元素中提取的值初始化它们。
例如,以下示例声明变量 x、y 和 z,并将它们分别初始化为 getSomeObject().x、getSomeObject().y 和 getSomeObject().z:
var { x, y, z } = getSomeObject();解构声明也适用于从数组中提取值:
var [x, y, z = 10] = getSomeArray();类似地,解构可用于函数参数声明:
function drawText({ text = "", location: [x, y] = [0, 0], bold = false }) {
// 绘制文本
}
// 使用对象字面量调用 drawText
var item = { text: "someText", location: [1, 2, 3], style: "italics" };
drawText(item);赋值
解构模式也可以用在常规赋值表达式中。 例如,交换两个变量可以写成一个解构赋值:
var x = 1;
var y = 2;
[x, y] = [y, x];namespace 关键字
TypeScript 使用 module 关键字来定义“内部模块”和“外部模块”; 这对刚接触 TypeScript 的开发者来说有点困惑。 “内部模块”更接近大多数人所说的命名空间;同样,JS 语境中的“外部模块”实际上就是现在的模块。
注意:定义内部模块的旧语法仍然支持。
之前:
module Math {
export function add(x, y) { ... }
}之后:
namespace Math {
export function add(x, y) { ... }
}let 和 const 支持
当目标为 ES3 和 ES5 时,现在支持 ES6 的 let 和 const 声明。
Const
const MAX = 100;
++MAX; // 错误:递增或递减运算符的操作数不能是常量。块级作用域
if (true) {
let a = 4;
// 使用 a
} else {
let a = "string";
// 使用 a
}
alert(a); // 错误:在此作用域中未定义 a。for..of 支持
TypeScript 1.5 增加了对数组的 ES6 for..of 循环在 ES3/ES5 中的支持,以及在目标为 ES6 时对迭代器接口的全面支持。
示例
当目标为 ES3/ES5 时,TypeScript 编译器会将 for..of 数组转换为惯用的 ES3/ES5 JavaScript:
for (var v of expr) {
}将被输出为:
for (var _i = 0, _a = expr; _i < _a.length; _i++) {
var v = _a[_i];
}装饰器
TypeScript 装饰器基于 ES7 装饰器提案。
装饰器是:
- 一个表达式
- 求值为一个函数
- 该函数接受目标、名称和属性描述符作为参数
- 并可选择返回一个要安装在目标对象上的属性描述符
更多信息,请参阅装饰器提案。
示例
装饰器 readonly 和 enumerable(false) 将在属性 method 安装到类 C 之前应用于它。 这允许装饰器更改实现,并在本例中增强描述符,使其变为 writable: false 和 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;
};
}计算属性
使用动态属性初始化对象可能有点麻烦。请看以下示例:
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 };
}这里我们需要创建一个变量来保存邻居映射以便初始化。 使用 TypeScript 1.5,我们可以让编译器承担繁重的工作:
function makeNode(name: string, initialNeighbor: Node): Node {
return {
name: name,
neighbors: {
[initialNeighbor.name]: initialNeighbor,
},
};
}支持 UMD 和 System 模块输出
除了 AMD 和 CommonJS 模块加载器,TypeScript 现在支持输出 UMD(通用模块定义)和 System 模块格式。
用法:
tsc --module umd
和
tsc --module system
字符串中的 Unicode 码点转义
ES6 引入了允许用户使用单个转义来表示 Unicode 码点的转义。
例如,考虑需要转义包含字符 '𠮷' 的字符串。 在 UTF-16/UCS2 中,'𠮷' 表示为一个代理对,这意味着它使用一对 16 位码元值进行编码,具体为 0xD842 和 0xDFB7。 以前这意味着你必须将码点转义为 "\uD842\uDFB7"。 这有一个主要缺点,即很难从代理对中区分两个独立的字符。
使用 ES6 的码点转义,你可以用一个转义干净地表示字符串和模板字符串中的确切字符:"\u{20bb7}"。 TypeScript 将在 ES3/ES5 中将字符串输出为 "\uD842\uDFB7"。
ES3/ES5 中的带标签模板字符串
在 TypeScript 1.4 中,我们为所有目标添加了模板字符串支持,并为仅 ES6 添加了带标签模板。 感谢 @ivogabe 所做的相当多的工作,我们在 ES3 和 ES5 中弥合了带标签模板的差距。
当目标为 ES3/ES5 时,以下代码
function oddRawStrings(strs: TemplateStringsArray, n1, n2) {
return strs.raw.filter((raw, index) => index % 2 === 1);
}
oddRawStrings`Hello \n${123} \t ${456}\n world`;将被输出为
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 依赖可选名称
/// <amd-dependency path="x" /> 告知编译器存在一个非 TS 模块依赖,需要注入到结果模块的 require 调用中; 但是,没有办法在 TS 代码中使用该模块。
新的 amd-dependency name 属性允许为 amd 依赖传递一个可选名称:
/// <amd-dependency path="legacy/moduleA" name="moduleA"/>
declare var moduleA: MyType;
moduleA.callStuff();生成的 JS 代码:
define(["require", "exports", "legacy/moduleA"], function (
require,
exports,
moduleA
) {
moduleA.callStuff();
});通过 tsconfig.json 支持项目
在目录中添加 tsconfig.json 文件表示该目录是 TypeScript 项目的根目录。 tsconfig.json 文件指定了根文件和编译项目所需的编译器选项。项目通过以下方式之一进行编译:
- 在不带输入文件的情况下调用 tsc,此时编译器从当前目录开始向上查找父目录链,搜索 tsconfig.json 文件。
- 在不带输入文件的情况下调用 tsc,并带有一个 -project(或简称 -p)命令行选项,该选项指定包含 tsconfig.json 文件的目录的路径。
示例
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"sourceMap": true
}
}有关更多详细信息,请参阅 tsconfig.json wiki 页面。
--rootDir 命令行选项
选项 outDir 在输出中复制输入层次结构。 编译器计算输入文件的最长公共路径作为输入文件的根; 然后使用该根在输出中复制所有子结构。
有时这不理想,例如输入 FolderA\FolderB\1.ts 和 FolderA\FolderB\2.ts 将导致输出结构镜像 FolderA\FolderB\。 现在如果向输入添加一个新文件 FolderA\3.ts,输出结构将弹出以镜像 FolderA\。
rootDir 指定要在输出中镜像的输入目录,而不是计算它。
--noEmitHelpers 命令行选项
TypeScript 编译器在需要时会输出一些辅助函数,如 __extends。 辅助函数在引用它们的每个文件中输出。 如果你想将所有辅助函数合并到一个地方,或覆盖默认行为,请使用 noEmitHelpers 指示编译器不要输出它们。
--newLine 命令行选项
默认情况下,输出换行符在 Windows 系统上是 \r\n,在 *nix 系统上是 \n。 newLine 命令行标志允许覆盖此行为,并指定在生成的输出文件中使用的换行符。
--inlineSourceMap 和 inlineSources 命令行选项
inlineSourceMap 使源映射文件内联在生成的 .js 文件中,而不是放在独立的 .js.map 文件中。 inlineSources 允许额外将源 .ts 文件内联到 .js 文件中。