症状
近期我们发现一个定义 .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.ts
和env.d.ts
的内容,将类型声明和实际代码放在同一个文件中。
最终,我们选择了重命名 env.d.ts
—> global.d.ts
的方案,问题解决。
因为 env.ts
会被其他文件引用,修改这个文件名可能会带来大量的重构,而 env.d.ts
只是一个类型声明文件,重命名不会影响其他文件的引用。
这两个文件的作用也不一样,因此合并文件内容的做法也不合适。
所以我们最终选择了重命名 .d.ts
文件的方案来解决这个问题。
总结
在 TypeScript 工程中使用 .d.ts
声明文件时,如果遇到类型声明不生效的问题,可以检查是否存在同名的 .ts
文件。TypeScript 编译器会优先使用同名的 .ts
文件,从而导致 .d.ts
声明文件被忽略。
需要根据实际项目的情况,选择一个最合适的规避命名冲突的解决方案,如重命名文件或合并内容等。这样可以确保类型声明文件能够正常生效,避免在编码时出现类型错误提示。