When we use Typesript to write the code of Node.js, we need to compile it with tsc after writing the code, and then run it with Node.js. This is more troublesome, so we will use ts node to directly run the ts code, eliminating the compilation stage.
Do you think it's amazing how ts node can directly run ts code?
In fact, the principle is not difficult. Let's implement a TS node today.
Related basis
Implementing TS node requires three basic knowledge:
- require hook
- repl module, vm module
- ts compiler api
Let's learn these basics first
require hook
Node.js when a JS module is require d, Module.load and module.js will be called internally_ extensions['.js'],Module._compile these three methods, and then execute them.

Similarly, the ts module and json module are the same process, so we only need to modify the module_ The method of "extensions" can achieve the purpose of hook:
require.extensions['.ts'] = function(module, filename) { // Modify code module._compile(Modified code, filename); }
For example, we registered the TS processing function above, so that this method will be called when processing the TS module, so we can compile here. This is the principle that TS node can directly execute ts.
repl module
Node.js provides a repl module that can create a command-line interactive environment for Read, Evaluate, Print and Loop, which is a question and answer method. ts node also supports repl mode. You can write ts code directly and execute it. The principle is based on the extension of repl module.
The api of repl is as follows: create a repl interaction through the start method, specify the prompt prompt, and implement the processing logic of eval by yourself:
const repl = require('repl'); const r = repl.start({ prompt: '- . - > ', eval: myEval }); function myEval(cmd, context, filename, callback) { // Process the input command callback(null, Processed content); }
There is a context in the execution of repl, which is r.context. We use vm module to execute code in this context:
const vm = require('vm'); const res = vm.runInContext(Code to execute, r.context);
The combination of these two modules can realize the command line interaction of question and answer, and the compilation of ts can also be done in eval, so as to realize the direct execution of ts code.
ts compiler api
We mainly use the command line tool of tsc for ts compilation, but it also provides a compilation api called ts compiler api. When making tools, we need to directly call the compiler api to compile.
The api for converting ts code to js code is this:
const { outputText } = ts.transpileModule(ts code, { compilerOptions: { strict: false, sourceMap: false, // Other compilation options } });
Of course, ts also provides an api for type checking. Because there are many parameters, we will expand it in a later article. Here, only the api of transpileModule is enough.
After understanding the knowledge of require hook, repl, vm and ts compiler api, the implementation principle of TS node is ready. Next, let's implement it.
Implement TS node
Direct execution mode
We can use ts node + a ts file to directly execute the ts file. Its principle is to modify the require hook, that is, the module_ Extensions ['. ts'].
Compile ts in the require hook, and then directly execute the compiled js, so as to achieve the effect of directly executing TS files.
So we rewrite module_ The extensions['.ts'] method reads the contents of the file in it, then calls ts.transpileModule to convert ts to js, and then calls Module._. Compile to process the compiled js.
In this way, we can directly execute the ts module. The specific module path is executed through the command line parameters, which can be obtained by process.argv.
const path = require('path'); const ts = require('typescript'); const fs = require('fs'); const filePath = process.argv[2]; require.extensions['.ts'] = function(module, filename) { const fileFullPath = path.resolve(__dirname, filename); const content = fs.readFileSync(fileFullPath, 'utf-8'); const { outputText } = ts.transpileModule(content, { compilerOptions: require('./tsconfig.json') }); module._compile(outputText, filename); } require(filePath);
We prepare a TS file called test.ts:
const a = 1; const b = 2; function add(a: number, b: number): number { return a + b; } console.log(add(a, b));
Then use this tool hook.js to run:

As you can see, ts is successfully executed, which is the principle of ts node.
Of course, there are still many detailed logic, but the main principle is the require hook + ts compiler api.
repl mode
TS node supports starting a repl environment, inputting TS code interactively and executing it. Its principle is based on the extension of the repl module provided by Node.js, compiling ts in the custom eval function, and then executing JS code in the repl context using the api of vm.runInContext.
We also launch a repl environment, set the prompt and customize the eval implementation.
const repl = require('repl'); const r = repl.start({ prompt: '- . - > ', eval: myEval }); function myEval(cmd, context, filename, callback) { }
The implementation of eval is to compile the ts code as js, and then use vm.runInContext to execute the compiled js code. The executed context is specified as the context of repl:
function myEval(cmd, context, filename, callback) { const { outputText } = ts.transpileModule(cmd, { compilerOptions: { strict: false, sourceMap: false } }); const res = vm.runInContext(outputText, r.context); callback(null, res); }
At the same time, we can also extend the context of repl, such as injecting a who environment variable:
Object.defineProperty(r.context, 'who', { configurable: false, enumerable: true, value: 'God said to have light' });
Let's test the effect:

You can see that after execution, a repl environment is started, and the prompt is changed to -. - >. You can directly execute ts code and access the global variable who.
This is the general principle of TS node's repl mode: repl + vm + ts compiler api.
All codes are as follows:
const repl = require('repl'); const ts = require('typescript'); const vm = require('vm'); const r = repl.start({ prompt: '- . - > ', eval: myEval }); Object.defineProperty(r.context, 'who', { configurable: false, enumerable: true, value: 'God said to have light' }); function myEval(cmd, context, filename, callback) { const { outputText } = ts.transpileModule(cmd, { compilerOptions: { strict: false, sourceMap: false } }); const res = vm.runInContext(outputText, r.context); callback(null, res); }
summary
ts node can execute ts code directly without manual compilation. In order to understand it deeply, we implemented a simple ts node that supports direct execution and repl mode.
The principle of direct execution is through require hook, that is, module_ In extensions [ext], the code is converted through the ts compiler api and then executed. The effect is that the ts code can be executed directly.
The principle of repl is an extension based on the repl module of Node.js, which can customize the prompt, context, eval logic, etc. We compiled it with ts compiler api in eval, and then executed the compiled JS in the context of repl through vm.runInContext. The effect is that the ts code can be executed directly in repl.
Of course, there are still many details about the complete TS node, but we have understood the general principle, and also learned the knowledge of require hook, repl and vm module, ts compiler api and so on.