一、模块

模块在其自身的作用域里执行,而不是在全局作用域里;这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确地使用export形式之一导出它们。
模块是自声明的;两个模块之间的关系是通过在文件级别上使用imports和exports建立的

1.导出

1.导出声明

任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export关键字来导出。

2.导出语句

  1. export { ZipCodeValidator };
  2. export { ZipCodeValidator as mainValidator }

3.重新导出

扩展其它模块,并且只导出那个模块的部分内容

2.导入

模块的导入操作与导出一样简单

1.导入一个模块中的某个导出内容

  1. import { ZipCodeValidator } from "./ZipCodeValidator";
  2. import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";

2.将整个模块导入到一个变量,并通过它来访问模块的导出部分

  1. import * as validator from "./ZipCodeValidator";

3.具有副作用的导入模块

  1. // 这些模块可能没有任何的导出或用户根本就不关注它的导出
  2. import "./my-module.js";

4.默认导出

每个模块都可以有一个default导出。 默认导出使用default关键字标记;并且一个模块只能够有一个default导出。 需要使用一种特殊的导入形式来导入default导出。

3. export = 和 import = require()

CommonJS和AMD都有一个exports对象的概念,它包含了一个模块的所有导出内容。
export =语法定义一个模块的导出对象。 它可以是类,接口,命名空间,函数或枚举。
若要导入一个使用了export =的模块时,必须使用TypeScript提供的特定语法import module = require("module")

4.可选的模块加载和其它高级加载场景

编译器会检测是否每个模块都会在生成的JavaScript中用到。 如果一个模块标识符只在类型注解部分使用,并且完全没有在表达式中使用时,就不会生成require这个模块的代码。 省略掉没有用到的引用对性能提升是很有益的,并同时提供了选择性加载模块的能力。

Node.js里的动态模块加载
  1. import { ZipCodeValidator as Zip } from "./ZipCodeValidator";

require.js里的动态模块加载
  1. import * as Zip from "./ZipCodeValidator";

5.使用其它的JavaScript库

1.外部模块

使用module关键字并且把名字用引号括起来,方便之后import

  1. declare module "path" {
  2. export function normalize(p: string): string;
  3. export function join(...paths: any[]): string;
  4. export let sep: string;
  5. }
  6. import { sep } from 'path'

2.模块声明通配符

好像没用过

5.创建模块结构指导

  1. 尽可能地在顶层导出
  2. 如果仅导出单个 classfunction,使用 export default
  3. 如果要导出多个对象,把它们放在顶层里导出
  4. 使用命名空间导入模式当你要导出大量内容的时候
  5. 模块里不要使用命名空间

    二、命名空间

    “内部模块”现在叫做“命名空间。何使用module关键字来声明一个内部模块的地方都应该使用namespace关键字来替换 ```typescript namespace Validation { export interface StringValidator {

    1. isAcceptable(s: string): boolean;

    }

    const lettersRegexp = /^[A-Za-z]+$/; const numberRegexp = /^[0-9]+$/;

    export class LettersOnlyValidator implements StringValidator {

    1. isAcceptable(s: string) {
    2. return lettersRegexp.test(s);
    3. }

    } }

  1. <a name="Yf4Ls"></a>
  2. ## 1.分离到多文件
  3. 当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护。
  4. <a name="ZurpK"></a>
  5. ### 1.多文件中的命名空间
  6. ```typescript
  7. namespace Validation {
  8. export interface StringValidator {
  9. isAcceptable(s: string): boolean;
  10. }
  11. }

  1. /// <reference path="Validation.ts" />
  2. namespace Validation {
  3. const lettersRegexp = /^[A-Za-z]+$/;
  4. export class LettersOnlyValidator implements StringValidator {
  5. isAcceptable(s: string) {
  6. return lettersRegexp.test(s);
  7. }
  8. }
  9. }

涉及到多文件时,我们必须确保所有编译后的代码都被加载了。
第一种方式,把所有的输入文件编译为一个输出文件,需要使用--outFile标记:

  1. tsc --outFile sample.js Test.ts

第二种方式,我们可以编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件。 然后,在页面上通过<script>标签把所有生成的JavaScript文件按正确的顺序引进来

2.别名

  1. import stringValidator = Validation.StringValidator;

三、命名空间和模块

1.使用命名空间

命名空间是位于全局命名空间下的一个普通的带有名字的JavaScript对象

2.使用模块

像命名空间一样,模块可以包含代码和声明。 不同的是模块可以声明它的依赖

3.命名空间和模块的陷阱

1.不必要的命名空间

如果你想把命名空间转换为模块,它可能会像下面这个文件一样

  1. export namespace Shapes {
  2. export class Triangle { /* ... */ }
  3. export class Square { /* ... */ }
  4. }
  5. // 顶层的模块Shapes包裹了Triangle和Square。 对于使用它的人来说这是令人迷惑和讨厌的

模块文件本身已经是一个逻辑分组,并且它的名字是由导入这个模块的代码指定,所以没有必要为导出的对象增加额外的模块层。

四、模块解析

1. 相对 vs. 非相对模块导入

  1. 为你自己写的模块使用相对导入,这样能确保它们在运行时的相对位置
  2. 使用非相对路径来导入你的外部依赖

    2.模块解析策略

    共有两种可用的模块解析策略:NodeClassic。 你可以使用--moduleResolution标记指定使用哪种模块解析策略。 若未指定,那么在使用了--module AMD | System | ES2015时的默认值为Classic,其它情况时则为Node。

    Classic

    这种策略以前是TypeScript默认的解析策略。 现在,它存在的理由主要是为了向后兼容。
    相对导入的模块是相对于导入它的文件进行解析的。
    对于非相对模块的导入,编译器则会从包含导入文件的目录开始依次向上级目录遍历,尝试定位匹配的声明文件。

    Node.js如何解析模块

    https://typescript.bootcss.com/module-resolution.html#node

    TypeScript如何解析模块

    TypeScript是模仿Node.js运行时的解析策略来在编译阶段定位模块定义文件。

3.Base URL

设置baseUrl来告诉编译器到哪里去查找模块。 所有非相对模块导入都会被当做相对于baseUrl
baseUrl的值由以下两者之一决定:

  • 命令行中baseUrl的值(如果给定的路径是相对的,那么将相对于当前路径进行计算)
  • ‘tsconfig.json’里的baseUrl属性(如果给定的路径是相对的,那么将相对于‘tsconfig.json’路径进行计算)

    4.路径映射

    TypeScript编译器通过使用tsconfig.json文件里的"paths"来支持这样的声明映射