将节点代码库迁移到TypeScript:全局范围?

时间:2015-12-09 20:21:35

标签: javascript node.js typescript

我正在尝试将大型节点代码库迁移到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,就好像它是全局可用的一样

2 个答案:

答案 0 :(得分:0)

路易是对的。每个文件(使用CommonJS编译节点时)都是像自己的模块一样创建的。就像节点中的正常情况一样。

为了让这个工作,你可以做这样的事情:

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时,每个文件都是自己的自包含模块。

编辑由于有问题的编辑:

好了再看一下你的代码后,有几个原因可以编译,但无法正常运行:

  1. 如果您向编译器发送一组文件,则所有这些文件中的所有引用都将发送到编译器的所有.ts文件中的全部范围内的所有变量。这是令人困惑的,有时令人遗憾,但目前它是如何运作的。在您的情况下,这会导致node.d.ts中全局范围中定义的require函数在所有.ts文件中都可用,而不仅仅在 moduleC.ts ;如果从 moduleC.ts 中删除引用,则会在logger.ts中出现错误,因为例如未定义require函数。它还会导致 logger.ts 中定义的var logger = ..在其他.ts文件中可用。 Typescript假定它在运行时可用..编译节点时不理想,但在编写实际的打字稿而不是编译纯javascript时不是问题。实际上,因为它是纯javascript,编译器不会将其识别为节点模块,并将其视为通用javascript文件。

  2. 使用commonjs编译时,每个文件都由typescript编译器自行编译。如果找到任何引用 imports ,那么将遵循这些引用并用于键入和验证代码,但编译仍然基于每个文件完成({{ 1}}不是导入,它被编译器解释为由以字符串作为参数的函数赋值的变量。)

  3. 这就是它编译的原因,让我们继续讨论为什么它不能正常工作。

    编译器在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文件,您应该能够在项目的任何地方引用ErrorString等内容。

另一方面,必须导入外部模块才能被引用。这就是你想要做的。

内部和外部模块之间的区别在于外部模块使用importexport。 内部使用declare module/namespace/var

只需将"compilerOptions": {"module": "commonjs"}添加到tsconfig.json文件即可启用外部模块。