模块
1544字约5分钟
TypeScript
2023-02-01
✨ES模块语法
TypeScript 模块支持所有 ES 模块的语法,特别之处在于允许输出和输入类型。
✨输出类型
type A = "a";
type B = "b";
// 方法一
export { type A, type B };
// 方法二
export type { A, B };✨引用类型
import在一条语句中,可以同时输入类型和正常接口。这样很不利于区分类型和正常接口,容易造成混淆。为了解决这个问题,TypeScript 引入了两个解决方法。
(1) 在import语句输入的类型前面加上type关键字
import { type A, a } from "./a";(2) 使用import type语句,这个语句只能输入类型,不能输入正常接口。
// 正确
import type { A } from "./a";
// 报错
import type { a } from "./a";
// import type 语句也可以输入默认类型。
import type DefaultType from "moduleA";
// import type 在一个名称空间下,输入所有类型的写法如下。
import type * as TypeNS from "moduleA";✨importsNotUsedAsValues 编译设置
TypeScript 特有的输入类型(type)的 import 语句,编译成 JavaScript 时怎么处理呢?
TypeScript 提供了importsNotUsedAsValues编译设置项,有三个可能的值:
(1)remove:这是默认值,自动删除输入类型的 import 语句。
(2)preserve:保留输入类型的 import 语句。
(3)error:保留输入类型的 import 语句(与preserve相同),但是必须写成import type的形式,否则报错。
✨CommonJS 模块
CommonJS 是 Node.js 的专用模块格式,与 ES 模块格式不兼容。
✨import = 语句
TypeScript 使用import =语句输入 CommonJS 模块。
import fs = require("fs");
const code = fs.readFileSync("hello.ts", "utf8");上面示例中,使用import =语句和require()命令输入了一个 CommonJS 模块。模块本身的用法跟 Node.js 是一样的。
除了使用import =语句,TypeScript 还允许使用import * as [接口名] from "模块文件"输入 CommonJS 模块。
import * as fs from "fs";
// 等同于
import fs = require("fs");✨export = 语句
TypeScript 使用export =语句,输出 CommonJS 模块的对象,等同于 CommonJS 的module.exports对象。
let obj = { foo: 123 };
export = obj;export = 语句输出的对象,只能使用import =语句加载。
import obj = require("./a");
console.log(obj.foo); // 123✨模块定位
模块定位(module resolution)指的是确定 import 语句和 export 语句里面的模块文件位置。
模块定位有两种方法,一种称为 Classic 方法,另一种称为 Node 方法。可以使用编译参数moduleResolution,指定使用哪一种方法。
没有指定定位方法时,就看原始脚本采用什么模块格式。如果模块格式是 CommonJS(即编译时指定--module commonjs),那么模块定位采用 Node 方法,否则采用 Classic 方法(模块格式为 es2015、 esnext、amd, system, umd 等等)。
✨相对模块,非相对模块
加载模块时,目标模块分为相对模块(relative import)和非相对模块两种(non-relative import)。
相对模块指的是路径以/、./、../开头的模块。下面 import 语句加载的模块,都是相对模块。
import Entry from "./components/Entry";import { DefaultHeaders } from "../constants/http";import "/mod";
非相对模块指的是不带有路径信息的模块。下面 import 语句加载的模块,都是非相对模块。
import * as $ from "jquery";import { Component } from "@angular/core";
✨Classic 方法
Classic 方法以当前脚本的路径作为“基准路径”,计算相对模块的位置。比如,脚本a.ts里面有一行代码import { b } from "./b",那么 TypeScript 就会在a.ts所在的目录,查找b.ts和b.d.ts。
至于非相对模块,也是以当前脚本的路径作为起点,一层层查找上级目录。比如,脚本a.ts里面有一行代码import { b } from "b",那么就会查找b.ts和b.d.ts。
✨Node 方法
Node 方法就是模拟 Node.js 的模块加载方法。
相对模块依然是以当前脚本的路径作为“基准路径”。比如,脚本文件a.ts里面有一行代码let x = require("./b");,TypeScript 按照以下顺序查找。
当前目录是否包含
b.ts、b.tsx、b.d.ts。当前目录是否有子目录b,该子目录是否存在文件
package.json,该文件的types字段是否指定了入口文件,如果是的就加载该文件。当前目录的子目录b是否包含
index.ts、index.tsx、index.d.ts。
非相对模块则是以当前脚本的路径作为起点,逐级向上层目录查找是否存在子目录node_modules。比如,脚本文件a.js有一行let x = require("b");,TypeScript 按照以下顺序进行查找。
当前目录的子目录
node_modules是否包含b.ts、b.tsx、b.d.ts。当前目录的子目录
node_modules,是否存在文件package.json,该文件的types字段是否指定了入口文件,如果是的就加载该文件。当前目录的子目录
node_modules里面,是否包含子目录@types,在该目录中查找文件b.d.ts。当前目录的子目录
node_modules里面,是否包含子目录b,在该目录中查找index.ts、index.tsx、index.d.ts。
进入上一层目录,重复上面 4 步,直到找到为止。
✨路径映射
TypeScript 允许开发者在tsconfig.json文件里面,手动指定脚本模块的路径。
(1)baseUrl: baseUrl字段可以手动指定脚本模块的基准目录。
{
"compilerOptions": {
"baseUrl": "."
}
}(2)paths: paths字段指定非相对路径的模块与实际脚本的映射。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"jquery": ["node_modules/jquery/dist/jquery"]
}
}
}(3)rootDirs: rootDirs字段指定模块定位时必须查找的其他目录。
{
"compilerOptions": {
"rootDirs": ["src/zh", "src/de", "src/#{locale}"]
}
}✨tsc 的--traceResolution参数
由于模块定位的过程很复杂,tsc 命令有一个--traceResolution参数,能够在编译时在命令行显示模块定位的每一步。
tsc --traceResolution✨tsc 的--noResolve参数
tsc 命令的--noResolve参数,表示模块定位时,只考虑在命令行传入的模块。
举例来说,app.ts包含如下两行代码。
import * as A from "moduleA";
import * as B from "moduleB";使用下面的命令进行编译。
tsc app.ts moduleA.ts --noResolve上面命令使用--noResolve参数,因此可以定位到moduleA.ts,因为它从命令行传入了;无法定位到moduleB,因为它没有传入,因此会报错。
