esbuild 输出 Foo 未定义:检查 declare const enum
自有实现写了 declare const enum,类型检查正常,输出仍可能引用未定义的 Foo。只有该文件真正定义常量时才可去掉 declare,第三方声明不能照改。
TypeScript 能通过类型检查,esbuild 输出的 JavaScript 却在读取枚举成员时抛出 ReferenceError: Foo is not defined。若声明写成 declare const enum Foo,应检查是否把只声明类型形态的写法放进了需要生成运行时值的实现文件。
这条具体表现发生于 esbuild 0.27.0 的 TypeScript 转换。esbuild 不复制 TypeScript 的类型系统,不能把依赖类型解释的编译行为当成必然支持。单纯更新 target 或关闭类型诊断,都不能为输出补出缺失的 Foo。
declare 声明没有提供运行时实现
下面展示容易混用的形式:
export declare const enum Foo {
A = 1,
B = 2,
}
export function readA() {
return Foo.A;
}
类型检查器知道 Foo 的成员,不代表独立转换器一定能按相同方式替换使用点。declare 本身不负责生成一个运行时对象;输出里若仍留下 Foo.A,而别处又没有提供 Foo,执行时就会失败。
先确认真正出错的是编译后引用缺少值,而不是模块导入路径错了、产物没有部署或某个导出没有被打包。也要看声明来自自己的实现文件,还是第三方提供的 .d.ts;这决定了能否更改声明。
自有常量实现不需要宣告为外部值
如果枚举确实由这个实现文件定义,成员都是该文件掌握的常量,可以去掉误用的 declare:
export const enum Foo {
A = 1,
B = 2,
}
export function readA() {
return Foo.A;
}
该处理保留数值 1、2 与原来的成员名称,让转换器处理真正的 const enum 定义。不要顺手换成另一组字符串值,否则接口、持久化值或比较条件可能变化。项目仍应保留独立的 TypeScript 类型检查,esbuild 转换成功并不证明类型正确。
第三方声明文件描述的是别处提供的接口,不能批量删除 declare 来伪造实现。若库向消费端暴露环境常量枚举,应沿库实际支持的编译路径处理,或由库提供可供运行时读取的值导出;应用需要同时对齐 JavaScript 与声明文件,而不是只修改 node_modules 让当前报错暂时消失。
同样,不要把这条处理套到 Node 内置 strip-only 执行入口。那里对需要生成运行时代码的 enum 有另一组限制;这里针对的是 esbuild 转换与环境声明语义之间的边界。
验收检查原来失败的成员读取点及最终 JavaScript,确认不会留下没有定义的 Foo 引用,再按项目已有方式检查类型、模块导出及使用这些值的业务行为。如果这是库代码,还需验证真实消费端的构建;只看库自己的类型检查通过,不足以证明发布的两份接口一致。


