模块与声明文件

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

类型查找顺序

  1. 包自带的类型(package.jsontypes 字段)
  2. @types
  3. 本地声明文件

创建本地类型定义

// 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;

最佳实践

  1. 使用 ES 模块:避免使用命名空间
  2. 类型单独导入:使用 import type 优化打包
  3. 声明文件放在 types 目录:保持项目结构清晰
  4. 为第三方库贡献类型:提交到 DefinitelyTyped
  5. 使用路径别名:避免深层相对导入
// 推荐:类型单独导入
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";

typescript 模块 声明文件