You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用声明合并创建的TypeScript接口编译后合并内容丢失,相关类型导出异常

使用声明合并创建的TypeScript接口编译后合并内容丢失,相关类型导出异常

嘿,我之前也踩过这个坑!这其实是tsup处理声明文件的机制导致的——它在聚合各个文件的类型声明时,没有正确把模块扩展块里的接口合并到顶层接口上,而是原封不动保留了declare module的结构,使得最终的顶层MyType还是空接口,自然keyof MyType就变成never了。

下面给你几个亲测有效的解决思路:

一、改用TypeScript官方工具生成声明文件

tsup自带的声明生成逻辑在处理模块式声明合并时不够完善,不如直接用tsc来生成声明文件,步骤如下:

  1. 修改tsup配置文件(比如tsup.config.ts),禁用自动生成声明:
import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["src/index.ts"],
  format: ["esm"],
  dts: false, // 关闭tsup的声明生成功能
});
  1. 在package.json的构建脚本里,先让tsup编译代码,再用tsc单独生成声明:
{
  "scripts": {
    "build": "tsup && tsc --emitDeclarationOnly"
  }
}

这样TypeScript会自行处理所有声明合并逻辑,生成的index.d.ts里会出现完整合并后的MyType接口,MyTypeKeys也能正确识别foo字段。

二、调整声明合并的实现方式

如果不想改动构建流程,也可以通过调整声明写法来规避这个问题:

方式1:将合并内容集中到同一文件

把merged.ts里的接口扩展直接移到MyType.ts中:

// MyType.ts
export interface MyType {}

declare module "./MyType" {
  interface MyType {
    foo: string;
  }
}

这样tsup生成声明时,能直接拿到合并后的完整接口。

方式2:使用全局声明合并(仅适用于全局类型场景)

如果你的MyType是全局可用的类型,可以改成全局接口的形式:

// MyType.ts
declare global {
  interface MyType {}
}
export type MyType = globalThis.MyType;
// merged.ts
declare global {
  interface MyType {
    foo: string;
  }
}

这种方式不需要依赖模块扩展,合并逻辑更稳定,但只适合全局类型的场景。

三、优化tsconfig配置

你的tsconfig.json里rootDir设为.可能会引发路径解析问题,建议改成源码所在的src目录(如果你的代码都在src下):

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "include": ["src"],
  "compilerOptions": {
    "rootDir": "./src",
    "declaration": true,
    "baseUrl": "./src",
    "target": "esnext",
    "module": "ESNext"
  }
}

试试上面的方法,应该就能解决你的问题啦!

备注:内容来源于stack exchange,提问作者Danny Delott

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 08:54:29