此页内容

TypeScript-模块

耶温

1829字约6分钟

2024-09-12

TypeScript-模块

TypeScript 模块是一个强大的特性,它允许你将代码组织成独立的文件和命名空间,从而促进更好的代码管理、重用和封装。

简介

通俗来讲,任何包含 importexport 的文件都是模块。相反,没有这些声明的文件是非模块的,它们就是一个个全局脚本文件。

模块本身有一个单独的作用域,模块中的变量、函数、类等都是私有的,除非明确地使用 export 导出,否则它们在模块外部是不可见的。如果其他文件需要使用这些变量、函数或类,就需要使用 import 导入。

如果一个文件不包括 export 但是希望把其作为一个模块,可以使用 export {} 的语法来显式地声明该文件是一个模块。

在 Typescript 中支持所有的 ES 模块语法。除此之外还支持导入导出类型,以及模块别名等特性。

// a.ts 导出类型
export type A = number;

// b.ts 导入类型
import { A } from './a';
// 使用类型
let a: A = 1;

当我们编译上面代码时,可以只编辑 b.ts 即可。因为 tsc 会自动检测到 b.ts 依赖了 a.ts ,然后编译 a.ts

当然我们也可以使用 tsc 同时编译两个文件。

tsc b.ts

// 同时编译两个文件
tsc a.ts b.ts

import type

import 在一条语句中,可以同时输入类型 和 正常内容。

// a.ts 导出类型和值
export type A = number;
export const a = 1;

// b.ts 导入类型和值
import { A, a } from './a';
// 使用类型
let b: A = 1;
// 使用值
console.log(a , b);

如上,import 语句中同时导入了类型和值,这样不利于我们区分哪些是类型哪些是变量方法。

TypeScript 提供了 import type 语法,可以解决这个问题。我们可以在 import 导入的类型前面加上 type 关键字来表示这是一个类型导入。

// a.ts 导出类型和变量
export type A = number;
export const a = 1;

// b.ts 导入类型和值
import { type A, a } from './a';
// 使用类型
let b: A = 1;
// 使用值
console.log(a , b);

除此之外我们还可以直接使用 import type 来导入多个类型。不过需要注意的是,import type 只能导入类型,不能导入其他变量方法等。

// a.ts 导出类型和变量
export type A = number;
export type B = string;
export const a = 1;

// b.ts 
// 导入类型
import type { A, B} from './a';
// 导入值
import { a } from './a';

// 使用类型
let b: A = 1;
// 使用值
console.log(a , b);

import type 语句也可以输入默认类型。

import type defaultType from './a';

import type 在名称空间中,输入所有类型。

import type * as types from './a';

同时,导出类型 export 也有两种方式,一种是 export type ,另一种是 export { type } 。表示导出的内容都是类型。

// a.ts 导出类
type A = number;

// 方法1
export { type A };

// 方法2
export type { A };

CommonJS 模块

TypeScript 提供了 module 选项来指定模块系统。默认情况下,TypeScript 使用 ES6 模块系统,但是你也可以选择其他模块系统,比如 CommonJS 或 AMD。

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs"
  }
}

Common JS 是 Node.js 的模块系统,与 ES 模块格式不同。

在TypeScript 中,使用 import = 以及 require 来导入 CommonJS 模块。

import obj = require('./a');
// or
const fs = require('fs');

需要注意的是,import = 语法是 TypeScript 特有的,它允许我们使用 import 语句来导入 CommonJS 模块。

除此之外,TypeScript 还支持 import * as [接口名] from "模块文件"

import * as fs from "fs";
// 等同于
import fs = require("fs");

同样的,TypeScript 也支持 export = 语句,输出 CommonJS 模块。等同于 export default

let obj = { foo: 123 };

export = obj;

需要注意的是,export = 语句导出的内容,只能使用 import = 语句导入。

import obj = require('./a');

模块定位

模块定位(module resolution) 是 TypeScript 编译器用来查找模块文件的过程。TypeScript 编译器会根据模块的路径来查找模块文件,如果找不到模块文件,编译器会报错。

  1. 相对模块

相对模块的路径是相对于当前文件的路径。相对模块的路径以 ./.././ 开头。

import { A } from './a';
  1. 非相对模块

非相对模块指的是不带有路径信息的模块。下面 import 语句加载的模块,都是非相对模块。

import * as $ from "jquery"
import { Component } from "@angular/core";

模块定位有两种方法,一种为 Classic,另一种为 Node。可以使用 moduleResolution 选项来指定模块定位方法。如果没有指定,TypeScript 会根据模块系统的不同,自动选择 Classic 或 Node。

  1. Classic 方法
  • 相对模块:编译器会从当前文件所在的目录开始,根据相对路径查找模块文件。
  • 非相对模块:从当前脚本的路径为起点,一层一层向上查找,直到找到模块文件或者到达根目录。
  1. Node 方法
  • 相对模块:编译器会从当前文件所在的目录开始,根据相对路径查找模块文件。

    • 例如:import { A } from './a'
    • 当前目录是否包含 b.tsb.tsxb.d.ts
    • 当前目录是否有子目录 b ,该子目录是否存在文件 package.json ,该文件的 types 字段是否指定了入口文件,如果是的就加载该文件。
    • 当前目录的子目录b是否包含 index.tsindex.tsxindex.d.ts
  • 非相对模块:非相对模块则是以当前脚本的路径作为起点,逐级向上层目录查找是否存在子目录 node_modules

    • 例如:import { B } from 'b'
    • 当前目录的子目录 node_modules 是否包含 b.tsb.tsxb.d.ts
    • 当前目录的子目录 node_modules ,是否存在文件 package.json ,该文件的 types 字段是否指定了入口文件,如果是的就加载该文件。
    • 当前目录的子目录 node_modules 里面,是否包含子目录 @types ,在该目录中查找文件 b.d.ts
    • 当前目录的子目录 node_modules 里面,是否包含子目录 b ,在该目录中查找 index.tsindex.tsxindex.d.ts
    • 进入上一级目录,重复上述步骤。

路径映射

TypeScript 中,我们可以在 tsconfig.json 文件里面,手动指定脚本模块的路径。

  1. baseUrl:表示模块的基路径,所有非相对模块的路径都会基于该路径进行解析。
{
  "compilerOptions": {
    "baseUrl": "./"
  }
}

如上配置中,基准目录为 tsconfig.json 所在的目录。

  1. paths:指定非相对路径的模块与实际脚本的映射。
{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"],
      "jquery": ["node_modules/jquery/dist/jquery"]
    }
  }
}

上面的配置中,@/* 表示所有以 @ 开头的模块,都会被映射到 src 目录下。jquery 表示所有以 jquery 开头的模块,都会被映射到 node_modules/jquery/dist/jquery 目录下。

  1. rootDirs:指定模块定位时必须查找的其他目录。
{
  "compilerOptions": {
    "rootDirs": ["src", "out"]
  }
}

上面的配置中,指定了模块定位时,需要查找的不同的目录。

tsc 相关参数:

  • --traceResolution:打印模块定位的详细信息。能够在编译时在命令行显示模块定位的每一步。
  • --noResolve:禁止自动解析模块。只考虑在命令行传入的模块。