模块与声明文件
TypeScript 的模块系统基于 ES Module,声明文件(.d.ts)则用于为 JavaScript 库提供类型定义。
ES Module 模块系统
导出
// 命名导出
export const PI = 3.14159;
export function add(a: number, b: number): number {
return a + b;
}
export interface User {
name: string;
age: number;
}
export class Calculator {
// ...
}
// 默认导出
export default function greet(name: string): string {
return `Hello, ${name}!`;
}
// 重新导出
export { someFunction } from "./other-module";
export * from "./utils";
export * as utils from "./utils";导入
// 命名导入
import { PI, add, User } from "./math";
// 默认导入
import greet from "./greet";
// 混合导入
import React, { useState, useEffect } from "react";
// 重命名导入
import { add as sum } from "./math";
// 整体导入
import * as math from "./math";
console.log(math.PI);
// 类型导入(推荐用于只导入类型)
import type { User } from "./types";
import { type User, createUser } from "./user";动态导入
// 动态导入返回 Promise
async function loadModule(): Promise<void> {
const { add } = await import("./math");
console.log(add(1, 2));
}
// 条件导入
if (process.env.NODE_ENV === "development") {
import("./dev-tools").then(module => {
module.enableDebug();
});
}模块解析
相对导入
// 相对于当前文件
import { helper } from "./utils";
import { config } from "../config";
import { api } from "./services/api";非相对导入(模块导入)
// 从 node_modules 导入
import React from "react";
import { useState } from "react";
import lodash from "lodash";
// 路径别名(需要配置 tsconfig.json)
import { Button } from "@/components/Button";
import { api } from "@services/api";tsconfig.json 路径配置
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@services/*": ["src/services/*"]
}
}
}声明文件(.d.ts)
声明文件用于描述 JavaScript 代码的类型。
基本语法
// types.d.ts
// 声明变量
declare const API_URL: string;
// 声明函数
declare function greet(name: string): string;
// 声明类
declare class User {
name: string;
constructor(name: string);
greet(): string;
}
// 声明接口
interface Config {
url: string;
timeout: number;
}
// 声明类型别名
type ID = string | number;全局声明
// global.d.ts
// 扩展全局对象
declare global {
interface Window {
myApp: {
version: string;
init(): void;
};
}
// 全局变量
var DEBUG: boolean;
// 全局函数
function log(message: string): void;
}
// 必须导出才能使声明文件成为模块
export {};模块声明
// 为没有类型的模块声明类型
declare module "untyped-module" {
export function doSomething(value: string): number;
export const version: string;
}
// 通配符模块(用于非 JS 资源)
declare module "*.css" {
const styles: { [className: string]: string };
export default styles;
}
declare module "*.png" {
const src: string;
export default src;
}
declare module "*.svg" {
import { FC, SVGProps } from "react";
const content: FC<SVGProps<SVGSVGElement>>;
export default content;
}扩展现有模块
// 扩展 Express
import "express";
declare module "express" {
interface Request {
user?: {
id: string;
name: string;
};
}
}
// 扩展 Vue
import "vue";
declare module "vue" {
interface ComponentCustomProperties {
$api: typeof api;
}
}第三方库类型
@types 包
# 安装类型定义
npm install --save-dev @types/lodash
npm install --save-dev @types/node
npm install --save-dev @types/react类型查找顺序
- 包自带的类型(
package.json的types字段) @types包- 本地声明文件
创建本地类型定义
// types/untyped-lib.d.ts
declare module "untyped-lib" {
export interface Options {
timeout?: number;
retry?: boolean;
}
export function request(url: string, options?: Options): Promise<Response>;
export default class Client {
constructor(baseURL: string);
get(path: string): Promise<unknown>;
post(path: string, data: unknown): Promise<unknown>;
}
}命名空间
命名空间用于组织代码,在现代 TypeScript 中较少使用:
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return /\S+@\S+\.\S+/.test(s);
}
}
export class PhoneValidator implements StringValidator {
isValid(s: string): boolean {
return /^\d{10,11}$/.test(s);
}
}
}
// 使用
const emailValidator = new Validation.EmailValidator();
console.log(emailValidator.isValid("test@example.com"));命名空间 vs 模块
| 特性 | 命名空间 | ES 模块 |
|---|---|---|
| 作用域 | 全局 | 文件级别 |
| 加载方式 | 脚本标签 | 模块加载器 |
| 代码组织 | 单文件内 | 跨文件 |
| 现代项目 | 不推荐 | 推荐 |
三斜线指令
用于引用其他声明文件:
/// <reference types="node" />
/// <reference path="./global.d.ts" />
/// <reference lib="es2020" />
// 通常不需要手动使用,由编译器自动处理环境声明
声明运行时存在的值:
// 声明全局变量(由外部脚本注入)
declare const jQuery: (selector: string) => unknown;
declare const $: typeof jQuery;
// 声明环境常量
declare const __DEV__: boolean;
declare const __VERSION__: string;
// 声明 Node.js 环境变量
declare namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
API_URL: string;
PORT?: string;
}
}发布类型声明
package.json 配置
{
"name": "my-library",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
]
}生成声明文件
// tsconfig.json
{
"compilerOptions": {
"declaration": true,
"declarationDir": "./dist/types",
"emitDeclarationOnly": false
}
}声明文件最佳实践
// index.d.ts - 导出所有公共类型
// 导出类型
export interface User {
id: string;
name: string;
email: string;
}
export type UserRole = "admin" | "user" | "guest";
// 导出函数类型
export function createUser(data: Partial<User>): User;
export function deleteUser(id: string): Promise<void>;
// 导出类
export class UserService {
constructor(apiUrl: string);
getUser(id: string): Promise<User>;
listUsers(): Promise<User[]>;
}
// 导出常量
export const VERSION: string;
export const DEFAULT_TIMEOUT: number;最佳实践
- 使用 ES 模块:避免使用命名空间
- 类型单独导入:使用
import type优化打包 - 声明文件放在 types 目录:保持项目结构清晰
- 为第三方库贡献类型:提交到 DefinitelyTyped
- 使用路径别名:避免深层相对导入
// 推荐:类型单独导入
import type { User, Config } from "./types";
import { createUser } from "./user";
// 推荐:使用路径别名
import { Button } from "@/components/Button";
import { useAuth } from "@/hooks/useAuth";
// 不推荐:深层相对导入
import { Button } from "../../../../components/Button";