我正在尝试将大型节点代码库迁移到TypeScript。为了简化操作,我只想首先将.js文件重命名为.ts文件,修复任何语义问题,不需要太多重构,然后逐步改进代码库。
让我们考虑以下示例:
logger.js:
exports = function(string) {
console.log(string);
}
moduleA.js:
var logger = require('./logger')
exports.someAction = function() {
logger.log('i'm in module a')
}
moduleB.js:
//var logger = require('./logger')
exports.someOtherAction = function() {
logger.log('i'm in module B')
}
moduleC.js:
var moduleA = require('./moduleA');
var moduleB = require('./moduleB');
exports.run = function(string) {
moduleA.someAction();
moduleB.someOtherAction(); //exception here - logger not defined
}
因此,如果我执行moduleB.someOtherAction(),我将得到一个异常,因为logB没有在moduleB.js的范围内定义。
但是typescript编译得很好,因为logger是在moduleA中声明的,这是因为(如果我理解的话)typescript将所有这些文件视为单个编译单元。
无论如何,如果没有太多的重构,还是要避免这种情况吗?
更新
我创建了一个示例项目which can be found here 如果我运行typescript编译器,我得到没有错误,虽然logB在moduleB.ts注释掉:
g@w (master) ~/projects/ts-demo: gulp generate
[10:39:46] Using gulpfile ~/projects/ts-demo/gulpfile.js
[10:39:46] Starting 'generate'...
[10:39:46] Starting 'clean'...
[10:39:46] Finished 'clean' after 11 ms
[10:39:46] Starting '<anonymous>'...
[10:39:47] Finished '<anonymous>' after 1.4 s
[10:39:47] Finished 'generate' after 1.41 s
g@w (master) ~/projects/ts-demo:
更新2
好的,所以这是TypeScript deep dive book中所述的预期行为:
如果您现在在同一个项目中创建一个新文件bar.ts,TypeScript类型系统将允许您使用foo.ts中的变量foo,就好像它是全局可用的一样
答案 0 :(得分:0)
为了让这个工作,你可以做这样的事情:
logger.ts
export default (str: string) => console.log(str);
moduleA.ts
import logger from './logger';
export var someAction = () => {
logger("i'm in module a");
}
moduleB.ts
import logger from './logger';
export var someOtherAction = () => {
logger("i'm in module B");
}
moduleC.ts
import { someAction } from './moduleA';
import { someOtherAction } from './moduleB';
someAction();
someOtherAction();
我还使用了 tsconfig.json :
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true
},
"files": [
"logger.ts",
"moduleA.ts",
"moduleB.ts",
"moduleC.ts"
]
}
编译它(只需在包含tsconfig.json的文件夹中键入 tsc )会在每个.ts文件中生成一个.js文件。它应该编译并运行得很好。您需要从中获取的主要内容是,在编译node / CommonJS时,每个文件都是自己的自包含模块。
编辑由于有问题的编辑:
好了再看一下你的代码后,有几个原因可以编译,但无法正常运行:
如果您向编译器发送一组文件,则所有这些文件中的所有引用都将发送到编译器的所有.ts文件中的全部范围内的所有变量。这是令人困惑的,有时令人遗憾,但目前它是如何运作的。在您的情况下,这会导致node.d.ts中全局范围中定义的require
函数在所有.ts文件中都可用,而不仅仅在 moduleC.ts ;如果从 moduleC.ts 中删除引用,则会在logger.ts中出现错误,因为例如未定义require函数。它还会导致 logger.ts 中定义的var logger = ..
在其他.ts文件中可用。 Typescript假定它在运行时可用..编译节点时不理想,但在编写实际的打字稿而不是编译纯javascript时不是问题。实际上,因为它是纯javascript,编译器不会将其识别为节点模块,并将其视为通用javascript文件。
使用commonjs编译时,每个文件都由typescript编译器自行编译。如果找到任何引用或 imports ,那么将遵循这些引用并用于键入和验证代码,但编译仍然基于每个文件完成({{ 1}}不是导入,它被编译器解释为由以字符串作为参数的函数赋值的变量。)
这就是它编译的原因,让我们继续讨论为什么它不能正常工作。
编译器在commonjs模式下输出的每个文件都是它自己的&#34; node-module&#34;。如果您想要从节点中的另一个模块中访问一个模块(这实际上与typescript无关,它只是节点的工作方式),您将需要该文件。在 moduleB.ts (以及在moduleB.js中)没有迹象表明节点要说什么是记录器。所以你只需要记录器。
在你第二次更新问题时你所指的例子中没有使用commonjs,而且更多的是与typescripts内部模块有关 - 而不是节点模块,这是完全不同的事情。
所以简单的答案是,如果你想在节点中的另一个文件中使用一个文件,你必须要求它。只需取消注释 moduleB.ts 中的注释引用即可启动并运行所有内容。
在代码的当前状态下,typescript编译器对你没有多大帮助,因为它确实无法帮助你。代码接近纯javascript,要从编译器获得体面的帮助,您需要开始将其转换为typescript。第一步可能是用var something = require('./whatever');
替换var asdf = require("")
,这将使编译器可以检查引用并为您提供很多帮助。
我意识到这是一个罗嗦的答案,但你设法让自己陷入一种令人困惑且难以解释的行为。
答案 1 :(得分:0)
在打字稿中有一些叫做内部/外部模块的东西。
内部模块可以声明可以在项目中的任何位置引用的内容。想想typings / lib文件,您应该能够在项目的任何地方引用Error
和String
等内容。
另一方面,必须导入外部模块才能被引用。这就是你想要做的。
内部和外部模块之间的区别在于外部模块使用import
和export
。
内部使用declare module/namespace/var
。
只需将"compilerOptions": {"module": "commonjs"}
添加到tsconfig.json
文件即可启用外部模块。