编程前端开发人员

什么是 TypeScript 中的声明文件,何时以及为何要编写自定义的 d.ts 文件?如何为外部 JS 模块结构化用户类型描述?

用 Hintsage AI 助手通过面试

答复。

问题背景:

许多 JavaScript 生态系统中的库只提供原始的 js 文件,而没有自己的类型。为了描述第三方或自定义库以及全局变量的类型,TypeScript 实现了一种特殊格式的文件,扩展名为 .d.ts(声明文件)。它们已经成为确保项目中类型信息和类型安全的标准,适用于任何 js 模块之上。

问题:

如果没有为第三方 JS 模块定义类型,TypeScript 被迫将这些导入视为 any,这意味着你失去了所有静态类型检查的优势:在调用、不存在的字段、不正确的参数上的错误逻辑上能够通过编译,并且只在代码运行后才会被发现。也无法进行自动补全和类型导航。

解决方案:

通过声明文件,可以手动为任何 JS 代码描述类型:函数、类、对象、命名空间甚至全局常量。因此,无论外部库的来源如何,项目都保持类型安全。

代码示例:

// hello.d.ts declare module 'hello' { export function sayHello(name: string): string; } // app.ts import { sayHello } from 'hello'; sayHello('TypeScript'); // 类型安全

关键特点:

  • 将签名和结构类型的描述与实现(原始 JS 代码)分开;
  • 允许即使在第三方/旧版本构建中也能够引入严格类型;
  • 在 .d.ts 文件中禁止实现,只允许签名/描述。

具有挑战性的问题。

可以直接在声明文件中声明函数的实现吗?

不可以,声明文件中只允许声明结构和签名,而不能实现它们。任何函数体的存在都会触发编译错误。

// 不可以: declare function sum(a: number, b: number) { return a + b; }

如果流行的 npm 模块没有原始包中的类型,在哪里寻找它们?

在 DefinitelyTyped 仓库(npm 包 @types/<lib>):几乎所有流行的包都有以单独 npm 模块形式提供的当前类型定义。

可以使用 d.ts 文件描述全局变量(不通过 import)吗?

可以,通过 ambient declarations 机制,例如,declare var VERSION: string;。这对于描述 window.X、全局常量和变量非常方便。

类型错误和反模式

  • 将函数和类的体写入 d.ts 文件;
  • 描述不完整或过时的签名,以导致与真实结构的冲突;
  • 为一个模块/全局变量连接不同的类型定义,导致类型冲突。

真实案例

** 负面案例 项目使用没有类型定义的 JS 库。开发人员忘记了 d.ts 文件,通过 any 访问 API。在更新库时出现错误:旧的调用崩溃,但编译器没有注意到。

优点:

  • 快速启动,无需额外描述。

缺点:

  • 隐藏错误,隐式行为,在大量代码中调试困难。

** 正面案例 为当前库开发了自定义 d.ts 文件,签名保持最新状态,使用 IDE 的自动补全和导航。

优点:

  • 完全的类型安全,在 API 变更时错误会立即显现;
  • 加速开发,可以让新开发人员加入而无需深入研究 JS 代码。

缺点:

  • 需要单独支持 d.ts 文件,需要在更新 JS 库时保持同步。