Featured image of post 为 NPM 导出正确的 TypeScript 声明文件 (.d.ts)Featured image of post 为 NPM 导出正确的 TypeScript 声明文件 (.d.ts)

为 NPM 导出正确的 TypeScript 声明文件 (.d.ts)

设计分布在 npm 上的库,通过双 ESM/CJS 导出结构本地加载类型。

为什么声明文件很重要

当您发布用 TypeScript 编写的 npm 包时,使用者需要类型信息来获取 IntelliSense 和编译时检查。如果没有 .d.ts 文件,您的库实际上会被键入为 any,这从一开始就违背了使用 TypeScript 的目的。

声明文件描述了导出的形状,而无需运送实施源。它们可以在消费者项目中实现树摇动、文档悬停提示和严格的类型检查。

生成 .d.ts 文件

declaration 设置为 tsconfig.json 中的 true 时,TypeScript 编译器会生成声明文件。

{
  "compilerOptions": {
    "declaration": true,
    "declarationMap": true,
    "emitDeclarationOnly": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}
选项目的
占位符_0.js 输出一起发出 .d.ts 文件
占位符_0生成声明的源映射(在编辑器中转到定义)
占位符_0仅发出声明,不发出 JS(当另一个工具处理捆绑时使用)
占位符_0JS 和 .d.ts 文件的输出目录

运行构建:

npx tsc

输出结构反映了您的 src/ 布局。文件 src/index.ts 生成 dist/index.jsdist/index.d.ts

Package.json 类型字段

types 字段(或其别名 typings)指向条目声明文件。

{
  "name": "my-library",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts"
}

对于双 ESM/CJS 包exports 字段提供条件解析:

{
  "exports": {
    ".": {
      "import": {
        "types": "./dist/index.d.mts",
        "default": "./dist/index.mjs"
      },
      "require": {
        "types": "./dist/index.d.ts",
        "default": "./dist/index.js"
      }
    },
    "./subpath": {
      "import": {
        "types": "./dist/subpath.d.mts",
        "default": "./dist/subpath.mjs"
      },
      "require": {
        "types": "./dist/subpath.d.ts",
        "default": "./dist/subpath.js"
      }
    }
  }
}

这告诉 TypeScript 为 import 语句加载 index.d.mts ,为 require() 调用加载 index.d.ts

三斜线指令

在手写的环境声明中,使用三斜杠指令来引用其他类型。

/// <reference types="node" />
/// <reference path="./custom-types.d.ts" />

export function readConfig(path: string): Buffer;

这些仅在 .d.ts 文件中有效,在实现 .ts 文件中无效。对于生成的声明,请改为依赖普通的 import 语句。

在您的包裹中包含声明

package.json 中的 files 字段控制发布到 npm 的内容:

{
  "files": [
    "dist",
    "!dist/**/*.test.d.ts",
    "README.md",
    "LICENSE"
  ]
}

始终排除测试声明文件和源 .ts 文件,除非您打算发布它们。

测试你的声明

在发布之前,验证消费者是否可以正确解析您的类型。

# Create a test project
mkdir test-consumer && cd test-consumer
npm init -y
npm link ../my-library
import { MyType } from "my-library";

const x: MyType = { /* ... */ };

在测试项目中运行 npx tsc --noEmit 。如果编译没有错误,则您的声明是正确的。

常见陷阱

问题解决方案
.js 文件缺少 .d.ts在 tsconfig
消费者看到 any 而不是正确的类型验证 types 字段指向正确的条目
双 ESM/CJS 类型无法解决exports 与单独的 types 条件一起使用
声明源图已损坏添加 declarationMap: true
公共 API 中的私有类型泄露在内部类型前面加上 _ 前缀或使用 @internal JSDoc 标签

概括

正确生成和发布声明文件可确保您的 TypeScript 库提供一流的开发人员体验。在 tsconfig 中配置 declaration: true,在 package.json 中设置 types 字段,对双模块包使用 exports,并在发布之前始终针对消费者项目测试您的声明。