Migrating a node database to TypeScript: global scope?

I am trying to port a large node codebase to TypeScript. To simplify the task, I just want to start by renaming .js files to .ts files, fix any semantic problems without a lot of refactoring, and then gradually improve the code base.

Consider the following example:

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 } 

So, if I execute moduleB.someOtherAction (), I will get an exception because the logger is not defined in scope in moduleB.js.

But typescript compiles just fine, because logger is declared in moduleA, and that is because (if I understand correctly) typescript treats all these files as a single compilation unit.

Anyway, to avoid this without much refactoring?

Update

I created a sample project that can be found here. If I run the typescript compiler, I get no errors, although the logger is commented in 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: 

Update 2

So this is the expected behavior, as stated in the TypeScript deep dive book :

If you now create a new bar.ts file in the same project, you will be allowed to typecript type system to use the foo variable from foo.ts, as if it were available globally

+5
source share
2 answers

Louis is right. Each file (when compiling for node using CommonJS) is created as its own module. Like a normal case in node.

To make this work, you can do something like this:

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(); 

I also used tsconfig.json:

 { "compilerOptions": { "module": "commonjs", "noImplicitAny": true }, "files": [ "logger.ts", "moduleA.ts", "moduleB.ts", "moduleC.ts" ] } 

Compiling this (just type tsc in the folder containing tsconfig.json) will result in a single .js file for the .ts file. It should compile and work just fine. The main thing you need to learn from this is that each file is its own stand-alone compilation unit for node / CommonJS.

EDIT Due to relevant changes:

Well, looking at your code again, there are several reasons why this compiles, but does not work properly:

  • If you send a group of files to the compiler all the links in all these files, and all the variables in the global scope will be available in all .ts files sent to the compiler. This is confusing and sometimes regrettable, but at the moment this is how everything works. This in your case leads to the fact that the require function, defined in the global area in node.d.ts, is available in all .ts files, and not just in moduleC.ts; if the link is removed from the C.ts module, you will get an error in logger.ts because the require function is not defined, for example. This also causes var logger = .. , defined in logger.ts, to appear as available in other .ts files. Typescript assumes that it will be available at runtime. Not ideal when compiling for node, but not a problem when you write the actual Typescript without trying to compile pure javascript. In fact, since this is pure javascript, the compiler does not recognize it as a node module and treats it as a generic javascript file.

  • When compiling with commonjs, each file is compiled independently by the Typescript compiler. If any links or imports are found, they will be used and used to enter and verify the code, but compilation is still performed for each file ( var something = require('./whatever'); not an import, it is interpreted by the compiler as a variable is assigned by a function that takes a string as an argument.)

So, why it compiles, lets go on to why it doesn’t work then.

Each file issued by the compiler in commonjs mode represents its own "node module". If you want to have access to one module from another in node (this has nothing to do with Typescript, this is exactly how node works), you will need this file. in the module B..ts (and in this case in the module B.js) there are no indications to node to say what a registrar is. Therefore, you just need to require a registrar.

In the example you are talking about in your second update, the commonjs question is not used, and even more so it is related to typescripts internal modules - not node, which is completely different.

So, the simple answer is that if you want to use one file from another in node, you must require it. Just by uncommenting the commented link in the B.ts module, you have to start and run everything.

In the current state of your code, the Typescript compiler does not help you much, because it cannot do this. The code is close to pure javascript, to get decent help from the compiler, you will need to start converting it to typescript. The first step might be to replace var asdf = require("") with import asdf = require(""); This will allow the compiler to check the links and help you in returning.

I understand that this is a verbose answer, but you managed to create a confusing and difficult explanation for your behavior.

0
source

There is something called internal / external modules in typescript.

Internal modules can declare material that can be referenced anywhere in the project. Think of the typings / lib files, you should be able to reference things like Error and String everywhere in your project.

External modules, on the other hand, must be imported for reference. This is what you want to do.

The difference between internal and external modules is that external ones use import and export . Internal ones use declare module/namespace/var .

Just add "compilerOptions": {"module": "commonjs"} to your tsconfig.json file to enable external modules.

0
source

Source: https://habr.com/ru/post/1237791/


All Articles