模块
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 type和export 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);
}
使用仅类型导入有以下几个好处:
- 明确表达意图,让代码更易读
- 避免产生不必要的运行时代码
- 在某些构建工具配置下可以避免循环依赖问题
模块解析策略
TypeScript提供了两种模块解析策略:Classic和Node,我们可以在tsconfig.json中通过moduleResolution选项进行配置。
{
"compilerOptions": {
"moduleResolution": "node"
}
}
Classic:这是TypeScript早期的解析策略,现在已经很少使用。
Node:模拟NodeJS的模块解析机制,这是目前最常用的策略。在这种策略下,相对路径导入会直接查找对应文件,非相对路径导入会按照NodeJS的规则逐级向上查找node_modules目录。现代项目中还可以使用Node16、NodeNext或Bundler等更新的解析策略,它们提供了对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.exportsES6/ES2015:ES6标准模块格式,使用import和exportESNext:最新的ES模块标准AMD:用于浏览器端的异步模块加载UMD:兼容多种模块系统的通用格式
现代前端项目通常使用ESNext,由构建工具(如Webpack、Vite)进行进一步处理。