模块

TypeScript完全支持ES6模块语法,本篇笔记我们介绍TypeScript中模块的使用方法,以及一些TypeScript特有的模块相关功能。

ES6模块

TypeScript中的模块语法和ES6其实是完全一致的,一个文件就是一个模块,模块有自己的作用域,模块内的变量、函数、类等默认不会暴露到全局作用域,需要通过export导出后才能被其他模块使用。

导出模块成员

我们可以使用export关键字导出模块中的变量、函数、类、类型别名、接口等。

export const PI = 3.14159;

export function add(a: number, b: number): number {
  return a + b;
}

export class Calculator {
  multiply(a: number, b: number): number {
    return a * b;
  }
}

export interface Point {
  x: number;
  y: number;
}

export type ID = string | number;

也可以在文件末尾统一导出。

const PI = 3.14159;

function add(a: number, b: number): number {
  return a + b;
}

class Calculator {
  multiply(a: number, b: number): number {
    return a * b;
  }
}

export { PI, add, Calculator };

默认导出

每个模块可以有一个默认导出,使用export default语法。

export default class Logger {
  log(message: string): void {
    console.log(message);
  }
}

默认导出也可以是函数或值。

export default {
  apiUrl: "https://api.example.com",
  timeout: 5000,
};

导入模块成员

使用import关键字导入其他模块导出的成员。

import { PI, add, Calculator } from "./math";

console.log(PI);
console.log(add(1, 2));

const calc = new Calculator();
console.log(calc.multiply(3, 4));

导入默认导出的成员时不需要使用花括号。

import Logger from "./logger";

const logger = new Logger();
logger.log("Hello");

也可以同时导入默认导出和命名导出。

import Logger, { PI, add } from "./module";

重命名导入导出

导入和导出时都可以使用as关键字进行重命名。

// 导出时重命名
export { add as sum, PI as CirclePI };

// 导入时重命名
import { add as sum, PI as CirclePI } from "./math";

整体导入

可以使用* as语法将模块的所有导出作为一个对象导入。

import * as MathUtils from "./math";

console.log(MathUtils.PI);
console.log(MathUtils.add(1, 2));

仅类型导入导出

TypeScript提供了import typeexport type语法,用于明确表示导入导出的是类型而非值。这种语法在编译后会被完全移除,不会产生任何运行时代码。

export interface User {
  id: number;
  name: string;
}

export type Status = "active" | "inactive";
import type { User, Status } from "./types";

function printUser(user: User): void {
  console.log(user.name);
}

使用仅类型导入有以下几个好处:

  1. 明确表达意图,让代码更易读
  2. 避免产生不必要的运行时代码
  3. 在某些构建工具配置下可以避免循环依赖问题

模块解析策略

TypeScript提供了两种模块解析策略:ClassicNode,我们可以在tsconfig.json中通过moduleResolution选项进行配置。

{
  "compilerOptions": {
    "moduleResolution": "node"
  }
}

Classic:这是TypeScript早期的解析策略,现在已经很少使用。

Node:模拟NodeJS的模块解析机制,这是目前最常用的策略。在这种策略下,相对路径导入会直接查找对应文件,非相对路径导入会按照NodeJS的规则逐级向上查找node_modules目录。现代项目中还可以使用Node16NodeNextBundler等更新的解析策略,它们提供了对ES模块更好的支持。

路径映射

TypeScript支持通过tsconfig.json配置路径别名,简化模块导入路径。

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"],
      "@utils/*": ["./utils/*"],
      "@components/*": ["./components/*"]
    }
  }
}

配置后,我们可以使用别名导入模块。

import { formatDate } from "@utils/date";
import Button from "@components/Button";

注意:paths配置仅用于TypeScript编译器的类型检查和代码提示,实际打包时还需要在构建工具(如Webpack、Vite)中配置对应的别名解析。

声明文件与模块

当我们使用第三方JavaScript库时,TypeScript需要类型声明文件(.d.ts)来提供类型信息。

DefinitelyTyped

大多数流行的JavaScript库都有社区维护的类型声明,发布在@types命名空间下,下面是一个例子。

npm i lodash
npm i -D @types/lodash

安装后,TypeScript会自动识别这些类型声明。

自定义声明文件

对于没有类型声明的库,我们只能自己编写声明文件。

declare module "my-library" {
  export function doSomething(value: string): number;
  export const version: string;
}

此外,也可以使用通配符匹配模块名。

declare module "*.png" {
  const value: string;
  export default value;
}

declare module "*.svg" {
  const value: string;
  export default value;
}

模块输出格式

TypeScript可以将代码编译为多种模块格式,具体可以通过tsconfig.json中的module选项配置。

{
  "compilerOptions": {
    "module": "ESNext"
  }
}

常用的模块格式包括:

  • CommonJS:Node.js传统的模块格式,使用require()module.exports
  • ES6/ES2015:ES6标准模块格式,使用importexport
  • ESNext:最新的ES模块标准
  • AMD:用于浏览器端的异步模块加载
  • UMD:兼容多种模块系统的通用格式

现代前端项目通常使用ESNext,由构建工具(如Webpack、Vite)进行进一步处理。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。