Skip to content

TypeScript - 环境声明 (Ambients)

TypeScript 的强大之处来自于其静态类型系统。然而,许多 JavaScript 生态系统中的现有库是在没有 TypeScript 类型的情况下编写的。声明文件(使用 .d.ts 扩展名)弥合了这一差距。

声明文件为 TypeScript 编译器描述现有 JavaScript 代码的形状(类型、函数签名、类、变量)。它告诉 TypeScript 期望什么类型,从而在使用 JavaScript 库时,在 TypeScript 项目中实现类型检查、自动补全和重构支持。

声明文件只包含类型信息;它们没有实现代码,也不会被编译成 JavaScript 输出。它们纯粹是为了开发人员和编译器在开发过程中的便利。

有几种方法可以获取 JavaScript 库的声明文件:

  1. 随库打包: 许多用 TypeScript 编写或积极维护的现代 JavaScript 库现在直接在其 npm 包中包含自己的 .d.ts 文件。安装这类库时,TypeScript 会自动找到并使用这些类型。
  2. DefinitelyTyped (@types): 这是一个由社区驱动的巨大仓库,包含数千个流行 JavaScript 库的声明文件。你可以从 npm 的 @types 作用域下安装某个库(例如,lodash、react、node)的类型。例如:npm install --save-dev @types/lodash。TypeScript 编译器会自动在 node_modules/@types 中查找类型。
  3. 自己编写: 如果库或 DefinitelyTyped 中没有可用的类型,你可以自己编写一个 .d.ts 文件来描述你使用的库的部分。对于内部 JavaScript 代码或不太常见的库,这通常是必需的。

让我们以流行的 lodash 库为例。首先,安装库及其类型:

npm install lodash
npm install --save-dev @types/lodash

现在,你可以在你的 TypeScript 代码中导入和使用 lodash 函数,并获得完整的类型安全和自动补全:

main.ts
import _ from 'lodash';
const numbers = [1, 5, 8, 10, 1, 5];
const uniqueNumbers = _.uniq(numbers);
console.log(uniqueNumbers); // 输出: [ 1, 5, 8, 10 ]
const message = "hello world";
const capitalizedMessage = _.capitalize(message);
console.log(capitalizedMessage); // 输出: Hello world
// 尝试错误地使用函数 - TypeScript 会捕获错误:
// const result = _.uniq("not an array"); // 错误:类型 'string' 的参数不能赋值给类型 'List<any> | null | undefined' 的参数。

因为我们安装了 @types/lodash,TypeScript 理解 _.uniq、_.capitalize 和其他 lodash 函数的签名。

当你需要描述没有现成类型的 JavaScript 代码时,可以在 .d.ts 文件中(有时对于简单情况也可以直接在 .ts 文件中)使用 declare 关键字。

想象一个简单的 JavaScript 库 my-js-lib.js:

// my-js-lib.js (假设此文件存在)
function greetLib(name) {
return `Hello from lib, ${name}!`;
}
const LIBRARY_VERSION = '1.0.0';

你可以创建一个对应的 my-js-lib.d.ts 文件:

// my-js-lib.d.ts
// 声明 JavaScript 文件中存在的函数
declare function greetLib(name: string): string;
// 声明存在的常量变量
declare const LIBRARY_VERSION: string;
// 你也可能声明模块、类、接口等
declare module 'some-other-module' {
export function processData(data: any): void;
}

现在,在你的 TypeScript 代码中,假设 JavaScript 文件已通过某种方式加载(例如,通过 <script> 标签或打包工具),你可以使用这些声明的元素进行类型检查:

app.ts
/// <reference path="my-js-lib.d.ts" />
// (如果未使用模块系统或 tsconfig includes,可能需要三斜线引用)
const greeting = greetLib("TypeScript 用户");
console.log(greeting);
console.log(`库版本: ${LIBRARY_VERSION}`);
// const wrongGreeting = greetLib(123); // 错误:类型 'number' 的参数不能赋值给类型 'string' 的参数。

declare 关键字向 TypeScript 表明实现存在于其他地方,它应该信任所提供的类型签名。

  • 目的: 为现有 JavaScript 代码提供类型信息。
  • 扩展名: .d.ts
  • 内容: 仅包含类型声明(接口、类型、函数签名、declare var/let/const/function/class/module...),不包含实现代码。
  • 运行时: 对生成的 JavaScript 代码没有影响。
  • 获取方式: 随库打包、DefinitelyTyped (@types) 或手动编写。
  • declare 关键字: 在 .d.ts 文件中使用,用于描述环境(ambient,现有)代码。

理解和利用声明文件对于有效地将 TypeScript 集成到依赖于 JavaScript 库的项目中至关重要,它可以确保整个代码库的类型安全。