TypeScript实现对ECMAScript 2015模块的支持,并支持针对CommonJS,AMD和其他模块系统的下层代码生成。
TypeScript程序由一个或多个源文件组成。
SourceFile:
ImplementationSourceFile
DeclarationSourceFile
ImplementationSourceFile:
ImplementationScript
ImplementationModule
DeclarationSourceFile:
DeclarationScript
DeclarationModule
扩展名为“ .ts”的源文件是包含语句和声明的实现源文件,扩展名为“ .d.ts”的源文件是仅包含声明的声明源文件。
声明源文件是实现源文件的严格子集,用于以附加方式声明与现有JavaScript代码关联的静态类型信息。 它们完全是可选的,但是使TypeScript编译器和工具能够在将现有的JavaScript代码和库集成到TypeScript应用程序中时提供更好的验证和帮助。
编译TypeScript程序时,该程序的所有源文件都会一起处理。 不同源文件中的语句和声明可以相互依赖,可能以循环方式相互依赖。 默认情况下,为编译中的每个实现源文件生成一个JavaScript输出文件,但是声明源文件中不生成任何输出。
TypeScript编译器自动确定源文件的依赖关系,并将这些依赖关系包含在正在编译的程序中。根据“参考注释”和模块导入声明进行确定,如下所示:
依次将包含为依赖项的任何文件以引用方式分析其引用,直到确定所有依赖项为止。
不包含模块导入或导出声明的源文件被分类为脚本。 脚本形成单个全局命名空间,并且脚本中声明的实体在程序中的所有范围内。
ImplementationScript:
ImplementationScriptElementsopt
ImplementationScriptElements:
ImplementationScriptElement
ImplementationScriptElements ImplementationScriptElement
ImplementationScriptElement:
ImplementationElement
AmbientModuleDeclaration
ImplementationElement:
Statement
LexicalDeclaration
FunctionDeclaration
GeneratorDeclaration
ClassDeclaration
InterfaceDeclaration
TypeAliasDeclaration
EnumDeclaration
NamespaceDeclaration
AmbientDeclaration
ImportAliasDeclaration
DeclarationScript:
DeclarationScriptElementsopt
DeclarationScriptElements:
DeclarationScriptElement
DeclarationScriptElements DeclarationScriptElement
DeclarationScriptElement:
DeclarationElement
AmbientModuleDeclaration
DeclarationElement:
InterfaceDeclaration
TypeAliasDeclaration
NamespaceDeclaration
AmbientDeclaration
ImportAliasDeclaration
组成全局命名空间的脚本的初始化顺序最终取决于在运行时加载生成的JavaScript文件的顺序(例如,可以由引用生成的JavaScript文件的
包含至少一个模块导入或导出声明的源文件被视为单独的模块。 在模块中声明的非导出实体仅在该模块的作用域内,但是可以使用导入声明将导出的实体导入其他模块。
ImplementationModule:
ImplementationModuleElementsopt
ImplementationModuleElements:
ImplementationModuleElement
ImplementationModuleElements ImplementationModuleElement
ImplementationModuleElement:
ImplementationElement
ImportDeclaration
ImportAliasDeclaration
ImportRequireDeclaration
ExportImplementationElement
ExportDefaultImplementationElement
ExportListDeclaration
ExportAssignment
DeclarationModule:
DeclarationModuleElementsopt
DeclarationModuleElements:
DeclarationModuleElement
DeclarationModuleElements DeclarationModuleElement
DeclarationModuleElement:
DeclarationElement
ImportDeclaration
ImportAliasDeclaration
ExportDeclarationElement
ExportDefaultDeclarationElement
ExportListDeclaration
ExportAssignment
模块的初始化顺序由所使用的模块加载程序确定,而TypeScript语言未指定。 但是,通常情况下,非循环相关的模块会以正确的顺序自动加载和初始化。
另外,可以在声明脚本中使用AmbientModuleDeclarations声明模块,该脚本直接将模块名称指定为字符串文字。 第12.2节将对此进行进一步描述。
下面是用单独的源文件编写的两个模块的示例:
// -------- main.ts --------
import { message } from "./log";
message("hello");
// -------- log.ts --------
export function message(s: string) {
console.log(s);
}
“ main”模块中的导入声明引用了“ log”模块,编译“ main.ts”文件会使“ log.ts”文件也作为程序的一部分进行编译。
TypeScript支持模块的多种JavaScript代码生成模式:
所需的模块代码生成模式是通过编译器选项选择的,并且不会影响TypeScript源代码。确实,有可能编写可被编译以在服务器端(例如,使用node.js)和客户端(使用AMD兼容加载程序)上使用的模块,而无需更改TypeScript源代码。
使用模块名称标识和引用模块。以下定义与CommonJS Modules 1.0规范中提供的定义一致。
为了解决模块引用,TypeScript将文件路径与每个模块关联。文件路径仅是模块源文件的路径,而没有文件扩展名。例如,包含在源文件“ C:\ src \ lib \ io.ts”中的模块具有文件路径“ C:/ src / lib / io”,而包含在源文件“ C:\ src \ ui \ editor.d.ts”的文件路径为“ C:/ src / ui / editor”。
导入声明中的模块名称解析如下:
导入声明用于从其他模块导入实体,并在当前模块中为其提供绑定。
表格的进口声明
import * as m from "mod";
导入具有给定名称的模块,并为模块本身创建本地绑定。 本地绑定分为一个值(代表模块实例)和一个命名空间(代表类型和命名空间的容器)。
表格的进口声明
import { x, y, z } from "mod";
导入给定的模块并为模块的导出成员的指定列表创建本地绑定。 指定的名称必须每个都引用给定模块的导出成员集中(11.3.4.4)中的一个实体。 本地绑定与其表示的实体具有相同的名称和分类,除非使用as子句指定不同的本地名称:
import { x as a, y as b } from "mod";
表格的进口声明
import d from "mod";
与进口申报完全相同
import { default as d } from "mod";
表格的进口声明
import "mod";
导入给定的模块而不创建任何本地绑定(仅在导入的模块有副作用的情况下才有用)。
存在导入要求声明以实现与早期版本的TypeScript的向后兼容性。
ImportRequireDeclaration:
import BindingIdentifier = require ( StringLiteral ) ;
导入require声明引入了引用给定模块的本地标识符。 导入require声明中指定的字符串文字将解释为模块名称(第11.3.1节)。 声明引入的本地标识符成为从引用模块导出的实体的别名,并按照与之完全相同的方式进行分类。 具体来说,如果引用的模块不包含导出分配,则将标识符分类为值和命名空间,如果引用的模块包含导出分配,则将标识符进行分类,就像在导出分配中命名的实体一样。
导入需要以下形式的声明
import m = require("mod");
等效于ECMAScript 2015导入声明
import * as m from "mod";
前提是所引用的模块不包含导出分配。
导出声明声明一个或多个导出模块成员。 可以使用导入声明(11.3.2)将模块的导出成员导入其他模块。
在模块的主体中,声明可以通过包含export修饰符来导出声明的实体。
ExportImplementationElement:
export VariableStatement
export LexicalDeclaration
export FunctionDeclaration
export GeneratorDeclaration
export ClassDeclaration
export InterfaceDeclaration
export TypeAliasDeclaration
export EnumDeclaration
export NamespaceDeclaration
export AmbientDeclaration
export ImportAliasDeclaration
ExportDeclarationElement:
export InterfaceDeclaration
export TypeAliasDeclaration
export AmbientDeclaration
export ImportAliasDeclaration
除了在模块的本地声明空间中引入名称之外,导出的声明还在模块的导出声明空间中引入具有相同分类的相同名称。 例如,声明
export function point(x: number, y: number) {
return { x, y };
}
引入了引用该函数的本地名称点和导出的名称点。
导出默认声明为导出名为default的实体提供了简化的语法。
ExportDefaultImplementationElement:
export default FunctionDeclaration
export default GeneratorDeclaration
export default ClassDeclaration
export default AssignmentExpression ;
ExportDefaultDeclarationElement:
export default AmbientFunctionDeclaration
export default AmbientClassDeclaration
export default IdentifierReference ;
函数,生成器或类的ExportDefaultImplementationElement或ExportDefaultDeclarationElement会在包含模块的导出声明空间中引入一个名为default的值,对于类而言,则引入一个名为default的类型。 声明可以选择为导出的函数,生成器或类指定本地名称。 例如,声明
export default function point(x: number, y: number) {
return { x, y };
}
引入了引用该函数的本地名称点和默认导出名称。 该声明实际上等效于
function point(x: number, y: number) {
return { x, y };
}
export default point;
再次等同于
function point(x: number, y: number) {
return { x, y };
}
export { point as default };
由单个标识符组成的表达式的ExportDefaultImplementationElement或ExportDefaultDeclarationElement必须命名在当前模块或全局命名空间中声明的实体。 该声明在包含模块的导出声明空间中引入了一个名为default的实体,其类别与引用的实体相同。 例如,声明
interface Point {
x: number;
y: number;
}
function Point(x: number, y: number): Point {
return { x, y };
}
export default Point;
引入一个本地名称Point和一个默认的导出名称,同时具有值和类型含义。
除单个标识符外,任何表达式的ExportDefaultImplementationElement都会在包含模块的导出声明空间中引入一个名为default的值。 例如,声明
export default "hello";
引入一个名为default的字符串类型的导出值。
导出列表声明从当前模块或指定模块中导出一个或多个实体。
ExportListDeclaration:
export * FromClause ;
export ExportClause FromClause ;
export ExportClause ;
不包含FromClause的ExportListDeclaration将从当前模块中导出实体。 在表格的声明中
export { x };
名称x必须引用在当前模块或全局命名空间中声明的实体,并且声明在包含模块的导出声明空间中引入具有相同名称和含义的实体。
带有FromClause的ExportListDeclaration从指定模块重新导出实体。 在表格的声明中
export { x } from "mod";
名称x必须引用指定模块的导出成员集中的实体,并且声明在包含模块的导出声明空间中引入具有相同名称和含义的实体。 没有为x创建本地绑定。
ExportListDeclaration的ExportClause可以指定多个实体,还可以选择指定用于导出实体的不同名称。 例如,声明
export { x, y as b, z as c };
在包含模块的导出声明空间中引入名为x,b和c的实体,其含义分别与分别名为x,y和z的本地实体相同。
指定*而不是ExportClause的ExportListDeclaration称为导出星形声明。 导出星号声明会重新导出指定模块的所有成员。
export * from "mod";
显式导出的成员优先于使用导出星形声明重新导出的成员,如下节所述。
通过从一组空的成员E和一组空的已处理模块P开始,然后按如下所述处理模块,以形成E中的全部已导出成员,来确定特定模块的导出成员集。 M包含以下步骤:
模块的实例类型是一种对象类型,该对象类型具有模块导出成员集中每个成员的表示值的属性。
如果模块包含导出分配,则该模块也包含导出声明是错误的。 两种出口是互斥的。
存在导出分配是为了与TypeScript的早期版本向后兼容。 导出分配将模块成员指定为要导出的实体,以代替模块本身。
ExportAssignment:
export = IdentifierReference ;
可以使用导入需求声明(11.3.3)导入包含导出分配的模块,然后导入需求声明引入的本地别名将具有导出分配中命名的标识符的所有含义。
包含导出分配的模块也可以使用常规导入声明(11.3.2)导入,前提是导出分配中引用的实体声明为命名空间或带有类型注释的变量。
假设以下示例位于文件“ point.ts”中:
export = Point;
class Point {
constructor(public x: number, public y: number) { }
static origin = new Point(0, 0);
}
当在另一个模块中导入“ point.ts”时,导入别名引用导出的类,并且可以用作类型和构造函数:
import Pt = require("./point");
var p1 = new Pt(10, 20);
var p2 = Pt.origin;
注意,不要求导入别名使用与导出实体相同的名称。
CommonJS Modules定义指定了一种方法,该方法用于编写具有隐式隐私,可以导入其他模块以及显式导出成员的JavaScript模块。 符合CommonJS的系统提供了一个’require’函数,该函数可用于同步加载其他模块以获取其单例模块实例,以及一个’exports’变量,模块可以向其添加属性以定义其外部API。
上面的11.3节中的“ main”和“ log”示例在为CommonJS Modules模式编译时生成以下JavaScript代码:
文件main.js:
var log_1 = require("./log");
log_1.message("hello");
文件log.js:
function message(s) {
console.log(s);
}
exports.message = message;
模块导入声明在生成的JavaScript中表示为变量,该变量通过调用模块系统主机提供的“ require”函数进行初始化。 仅当导入模块或引用导入模块的本地别名(第10.3节)被引用为导入模块主体中的PrimaryExpression时,才会为特定的导入模块生成变量声明和’require’调用。 如果仅将导入的模块引用为NamespaceName或TypeQueryExpression,则不会生成任何内容。
一个例子:
文件geometry.ts:
export interface Point { x: number; y: number };
export function point(x: number, y: number): Point {
return { x, y };
}
game.ts文件:
import * as g from "./geometry";
let p = g.point(10, 20);
“游戏”模块在表达式中(通过其别名“ g”)引用导入的“几何”模块,因此在生成的JavaScript中包含了“要求”调用:
var g = require("./geometry");
var p = g.point(10, 20);
取而代之的是将“游戏”模块写为仅在类型位置引用“geometry”:
import * as g from "./geometry";
let p: g.Point = { x: 10, y: 20 };
生成的JavaScript将不依赖于’geometry’模块,而仅仅是
var p = { x: 10, y: 20 };
异步模块定义(AMD)规范扩展了CommonJS Modules规范,提供了一种模式,用于编写具有相关性的异步可加载模块。 使用AMD模式,模块将作为对全局“定义”函数的调用而生成,该函数接受一系列依赖关系(指定为模块名称)以及包含模块主体的回调函数。 全局“定义”功能是通过在应用程序中包含符合AMD要求的加载程序来提供的。 加载程序安排异步加载模块的依赖项,并在完成后调用回调函数,将已解析的模块实例作为自变量按其在依赖项数组中列出的顺序传递为参数。
当为AMD模式编译时,上面的“ main”和“ log”示例生成以下JavaScript代码。
文件main.js:
define(["require", "exports", "./log"], function(require, exports, log_1) {
log_1.message("hello");
}
log.js文件:
define(["require", "exports"], function(require, exports) {
function message(s) {
console.log(s);
}
exports.message = message;
}
始终存在特殊的“需要”和“导出”依赖项。 根据需要,其他条目会添加到依赖项数组和参数列表中,以表示导入的模块。 与CommonJS Modules的代码生成类似,只有在导入模块主体中某处将导入模块引用为PrimaryExpression时,才会为特定的导入模块生成依赖项。 如果仅将导入的模块引用为NamespaceName,则不会为该模块生成依赖关系。