老胡茶室
老胡茶室

排错:TypeScript 工程文件中 .d.ts 声明文件不生效

冯宇

症状

近期我们发现一个定义 .d.ts 声明文件的 TypeScript 工程中,声明的类型没有生效。

例如我们定义了一个 env.d.ts 文件,内容如下:

/// <reference path="../.astro/types.d.ts" />

declare namespace App {
  // Note: 'import {} from ""' syntax does not work in .d.ts files.
  interface Locals {
    user: import("better-auth").User | null;
    session: import("better-auth").Session | null;
    isAdmin: boolean;
  }
}

但是在编码的时候,在 vscode 中总是提示类似于这样的错误:

类型“Locals”上不存在属性“user”。ts(2339)

我们检查了 tsconfig.json 的配置,确认这个文件已经包含在编译范围内,但是依旧没有生效。所以我们进行了进一步的排查。

原因

经过一番研究我们发现,原来是我们的工程中的同级目录下存在一个名为 env.ts 的文件。经过我们的研究,发现 TypeScript 编译器在处理 .d.ts 声明文件时,会优先考虑同名的 .ts 文件。

这意味着如果存在一个同名的 .ts 文件,TypeScript 编译器会优先使用它,而忽略同名的 .d.ts 声明文件。

因此,在我们的工程中,env.ts 文件覆盖了 env.d.ts 的声明,导致我们在编码时无法使用 env.d.ts 中定义的类型。

解决

为了解决这个问题,有以下几种方案:

  • 重命名 env.ts 文件,避免与 env.d.ts 同名。
  • 重命名 env.d.ts 文件,避免与 env.ts 同名。
  • 合并 env.tsenv.d.ts 的内容,将类型声明和实际代码放在同一个文件中。

最终,我们选择了重命名 env.d.ts —> global.d.ts 的方案,问题解决。

因为 env.ts 会被其他文件引用,修改这个文件名可能会带来大量的重构,而 env.d.ts 只是一个类型声明文件,重命名不会影响其他文件的引用。

这两个文件的作用也不一样,因此合并文件内容的做法也不合适。

所以我们最终选择了重命名 .d.ts 文件的方案来解决这个问题。

总结

在 TypeScript 工程中使用 .d.ts 声明文件时,如果遇到类型声明不生效的问题,可以检查是否存在同名的 .ts 文件。TypeScript 编译器会优先使用同名的 .ts 文件,从而导致 .d.ts 声明文件被忽略。

需要根据实际项目的情况,选择一个最合适的规避命名冲突的解决方案,如重命名文件或合并内容等。这样可以确保类型声明文件能够正常生效,避免在编码时出现类型错误提示。