TypeScript ES6模块语法与环境声明d.ts模块

6

假设我有一个包含50个文件的模块,每个文件都包含以下内容:

export class <SomeClassName> { /* 内容 */ }

然后,我创建了一个根文件,通过重新导出所有文件来简化使用。因此,它看起来像这样:

export * from "./src/some-class-1";
export * from "./src/some-class-2";
export * from "./src/some-class-3";
// etc

然后,我通过TSC运行它,使用"common"模块目标和"es5"描述符输出。
到目前为止还不错。我的package.json文件名为my-module,将输出的index.js作为模块的入口点。然后,我决定在另一个TypeScript项目中使用这个模块。
所以我对它进行npm安装(假设),npm install my-module,它会自动拉取d.ts文件和实际的commonjs模块,因此我可以使用它们。一切看起来都很好。
然后出现了问题。我决定使用这个模块: import {SomeClass1} from 'my-module' 在TS世界中,它无法识别my-module,因为如果我们回过头来看一下输出的index.js,它并没有包含一个环境模块。
所以问题在于,通常情况下,common模块使用package.json作为包含模块名称的参考点,但是TS使用d.ts文件。那么我想好了,我需要在index.ts中将我的重新导出包装在一个模块中,所以我尝试: export module "my-module" { /* all other re-exports */ } 但是结果发现,你只能在环境模块中使用字符串模块名称,并且你只能将它们放在d.ts文件中,但是我的d.ts文件是从现有代码库生成的。
所以这是我的困境,我可以手动添加declare module "my-module"包装到我的d.ts中,但这不是很自动化,或者我可以像ES6语法的博客文章一样引用文件,这将导致大量的import {blah} from "../node_modules/my-module/dist/index",我们都希望达成共识,这有点傻。
因此,在使用ES6语法时,我找不到任何其他适用的方法,因为所有关于这个主题的博客文章和文档都使用相对文件导入,而不是通过d.ts文件共享。
那么,在这里是否有一种方法可以将我的重新导出包装在一个文本模块名称中?或者至少告诉index.d.ts包含一个环境模块?(请记住,它将为项目中的每个文件输出一个d.ts文件,但我们只关心通过index.d.ts导入模块,因为它重新导出了所有内容)。

你可以创建一个单独的 d.ts 文件,引用生成的文件并在其中声明你的环境模块。希望当人们转移到 System.js 时,这将不再是必需的。 - billc.cn
听起来有点像黑客技巧,因为我需要手动编写那个文件,然后将其隐藏在某个地方,并在构建完成后放入其中。如果没有其他解决方案,我想我可以研究一下这个问题,但希望有更优雅的解决方案,因为这似乎不是一个非常复杂的用例。此外,我目前也为系统模块类型进行构建,但似乎存在一些问题,而且我认为在没有大量运行时 babel 的情况下,使用 es6 模块使 nodejs 易于使用还需要一段时间。 - Grofit
我刚刚尝试了一下,因为它似乎失败了“错误 TS2439:环境模块声明中的导入或导出声明不能通过相对模块名称引用模块。” - Grofit
3个回答

3

目前您仍需要使用其他工具来生成单个的 .d.ts 文件。以下是几种可以实现该功能的工具:

我个人使用的是一个定制版本的 dts-generator,它利用了 tsconfig.json 并适用于夜间 TypeScript 构建。您可以在 这里 查看它为我的一个软件包生成的 .d.ts。


这个 bundle 看起来可能是解决方案,我会很快尝试并告诉你答案,如果它按预期工作的话。 - Grofit
目前似乎存在一个dts-bundle的bug(已在问题中提出),当您有非相对模块(即npm样式模块包含)时,它会停止编译。另一个dts-generator似乎不支持ES6语法,因此无法在此问题的上下文中使用。 - Grofit
请尝试在我的dts-generator分支(ts-next)中使用ES6语法和TypeScript的夜间构建版本(如果您想使用最新的稳定TypeScript版本,只需修改package.json)。我的fork链接在原始答案中。 - Vadim Macagon
我需要提供单独的文件还是只需包含baseDir?如果我这样做,它似乎找不到任何类型定义文件,所以我可以将baseDir用作所有d.ts文件的文件夹,并以某种方式提供类型定义吗?(我注意到引用语法的外部,但那不是很ES6) - Grofit
我在 tsconfig.json 中引用了所有的 .ts.d.ts 文件,将 baseDir 选项设置为包含 tsconfig.json 的目录,并将 excludes 选项设置为匹配我不想内联到生成的 d.ts 中的 .d.ts 文件(即使它们没有被内联,它们仍然是编译上下文的一部分,因此类型解析按预期工作)... 示例。由于我没有将 .d.ts 文件内联到生成的 .d.ts 中,因此我必须在使用生成的 .d.ts 的任何项目中显式包含所有相关的类型定义。 - Vadim Macagon
我不使用tsconfig文件,因为我输出到许多模块类型。我想我可以添加一个纯粹用于d.ts创建的文件。 - Grofit

3

好的,我终于让它工作了,至少对于CommonJS是这样的。

现在随着typescript 1.6版本的更改,TS模块解析将检查node_modules文件夹中是否有index.d.ts文件。所以,在构建过程的最后,我基本上添加了这个文件,它指向我的dist/definitions/index.d.ts文件,该文件包含了所有的导出内容。

这样你就可以像这样导入:import * from "my-module",只要在你的node_modules文件夹中有一个my-module文件夹,其中包含一个index.d.ts文件,它将把该索引中的所有内容视为一个模块。

如果有其他人找到一个适用于所有模块而不仅仅是commonjs的更好的解决方案,我会很高兴将答案更改为其他解决方案,但目前这已经足以让我继续下去,希望能帮助别人。


虽然在依赖于此类包含系统的项目中生成 d.ts 文件仍存在一些问题。 - Grofit
我正在尝试在一个AMD项目中实现类似的功能,但是我遇到了困难。我正在构建一个名为A的库,并将由该项目生成的d.ts文件手动复制到node_modules/A/中,并将主模块d.ts文件重命名为index.d.ts,但我仍然会收到编译器错误,指出“找不到模块A”。 - jaker

2
当你编译index.ts时,需要告诉TSC为你生成定义文件。 index.ts:
export * from "./src/some-class-1";
export * from "./src/some-class-2";
export * from "./src/some-class-3";

命令:

tsc --declaration index.ts

这应该会生成一个带有导出的索引d.ts文件,以及每个some-class-*d.ts文件和它们的导出。
当然,用户需要引用所有的d.ts文件才能使其工作,这是一件麻烦的事情;请参考这里。我相信您可以使用--out选项将所有文件合并成一个,其中包括d.ts。但是,我不确定这是否符合您的要求。

1
据我所知,只有在不使用模块输出时,out 才能正常工作。目前,我通过 gulp typescript 对所有文件执行上述命令,但是一个根本性的问题是,没有任何一个输出文件会包含模块声明。因此,index.d.ts 将得到正确的输出并具有所有导出项,但如果我尝试导入它们 import * from "my-module" ,它怎么知道 my-module 是什么呢?因为没有任何一个声明文件会包含它。 - Grofit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接