これは、なにをしたくて書いたもの?
Node.jsでソースコードを書く時は基本的にTypeScriptにしたいのですが、時々ちょっとしたスクリプトを書きたい時もあったりします。
そういう時は、ちゃんとした設定などは特に用意せず、さっと書いてそのまま実行したいのですが、そういう時はどういう手段が
あるのかなと思って、調べてみることにしました。
やりたいこと
TypeScriptファイルを実行時に直接トランスパイル、実行したいわけですね。
選択肢としては、ts-node、esbuild-register、tsxがあるようです。
今回はそれぞれをあまり深追いせず、さらっと見ていく感じにします。求めている用途がそういう傾向なので。
ただ、実行速度は気になるので、そこは動作時に合わせて見ていきたいと思います。
ts-node
ts-nodeは、Node.jsのTypeScript実行エンジン、REPLです。型チェックも行われます。
GitHub - TypeStrong/ts-node: TypeScript execution and REPL for node.js
使い方はこちらで、ts-node
というコマンドにTypeScriptファイルを渡して実行します。
また、SWCと組み合わせることもできるようです。
esbuild-register
esbuild-registerは、esbuildを使ってTypeScriptファイルを実行時にトランスパイルします。
Node.jsの-r
(--require
)オプションと組み合わせて使います。
tsconfig.json
が存在する場合、以下のオプションのみ使用するようです。また、型チェックは行いません。
It will use jsxFactory, jsxFragmentFactory and target options from your tsconfig.json
tsx
tsxは、TypeScriptファイルおよびESMファイルを実行できるものです。
GitHub - privatenumber/tsx: ⚡️ TypeScript Execute: Node.js enhanced to run TypeScript & ESM
tsxと書くとTSX(JSX)を連想しますが、こちらはTypeScript Executeの略でtsxだそうです。
REPLがあったり、Watch modeがあったりと機能がいろいろあるようです。
型チェックは行いません。有効なtsconfig.json
のプロパティは、esbuildに従います。
Content Types / TypeScript / TypeScript caveats / Only certain tsconfig.json fields are respected
見ていくのはこれくらいにして、実際に動かしてみようと思います。
環境
今回の環境は、こちら。
$ node --version v18.18.2 $ npm --version 9.8.1
お題
ソースコードが複数であっても読めることくらいは確認しておこうかなと思ったので、とりあえず以下の2つのファイルを用意。
index.ts
import { message } from "./func"; console.log(message('TypeScript'));
func.ts
export function message(word: string): string { return `Hello ${word}`; }
こちらを、ts-node、esbuild-register、tsxそれぞれで実行していきます。
ts-node
ts-nodeのインストール。TypeScriptに依存しているので、合わせてインストールしておきます。
$ npm i -D ts-node typescript
バージョン。
"devDependencies": { "ts-node": "^10.9.1", "typescript": "^5.3.2" }
実行。ts-node
コマンドを使います。
$ npx ts-node index.ts Hello TypeScript $ time npx ts-node index.ts Hello TypeScript real 0m1.264s user 0m2.270s sys 0m0.130s
動作しましたね。速度はこれだけで1秒を超えました。
ts-node+SWC
オマケで、ts-node+SWCも試してみましょう。インストール。
$ npm i -D ts-node typescript @swc/core @swc/helpers regenerator-runtime
バージョン。
"devDependencies": { "@swc/core": "^1.3.99", "@swc/helpers": "^0.5.3", "regenerator-runtime": "^0.14.0", "ts-node": "^10.9.1", "typescript": "^5.3.2" }
実行。--swc
オプションを追加します。
$ npx ts-node --swc index.ts Hello TypeScript $ time npx ts-node --swc index.ts Hello TypeScript real 0m0.653s user 0m0.689s sys 0m0.073s
ts-nodeのみの時と比べて、実行速度が半分くらいになりました。
esbuild-register
続いて、esbuild-register。インストール。
$ npm i -D esbuild esbuild-register
バージョン。
"devDependencies": { "esbuild": "^0.19.7", "esbuild-register": "^3.5.0" }
実行。-r
オプションに、esbuild-register
を指定します。
$ node -r esbuild-register index.ts Hello TypeScript $ time node -r esbuild-register index.ts Hello TypeScript real 0m0.145s user 0m0.122s sys 0m0.024s
速いですね。ts-nodeの10分の1くらいです。
tsx
最後はtsxです。インストール。
$ npm i -D tsx
バージョン。
"devDependencies": { "tsx": "^4.2.0" }
実行。tsx
コマンドで実行します。
$ npx tsx index.ts Hello TypeScript $ time npx tsx index.ts Hello TypeScript real 0m0.586s user 0m0.613s sys 0m0.084s
ts-nodeに比べると速いですが、esbuild-registerほどではないみたいです。
オマケ
最後にオマケで、npmパッケージを使う例も試しておきましょう。このパートは、ts-node、esbuild-register、tsxのそれぞれの
パターンを簡略化して書くことにします。
使うパッケージは、Expressとします。この用途でExpressはちょっと違うんじゃないのかなという気がしますが、コマンドライン系の
パッケージには詳しくないので…。
インストール。
$ npm i express $ npm i -D @types/express
バージョン。
"devDependencies": { "@types/express": "^4.17.21", ... }, "dependencies": { "express": "^4.18.2" }
ソースコードは、こちらにしました。ExpressのHello Worldですね。
app.ts
import express, { Express } from 'express'; const app: Express = express(); const port: number = 3000; app.get('/', (req, res) => { res.send('Hello World'); }); app.listen(port, () => { console.log(`start server, port = ${port}`); });
起動の様子は、それぞれ書いていきます。
## ts-node $ npx ts-node app.ts start server, port = 3000 ## ts-node+SWC $ npx ts-node --swc app.ts start server, port = 3000 ## esbuild-register $ node -r esbuild-register app.ts start server, port = 3000 ## tsx $ npx tsx app.ts start server, port = 3000
結果はいずれも同じです。
$ curl localhost:3000 Hello World
おわりに
TypeScriptコードを直接実行する方法として、ts-node、esbuild-register、tsxを試してみました。
ちゃんとしたものを作る場合は設定などをしっかり用意すると思うのですが、こうやってライトに実行できる方法も知っておくと便利
なのかなという気がします。
速度的にはesbuild-registerが良さそうですが、型チェックがないのがネックですね。とはいえ、そのあたりをこだわり始めると
そもそもこの方法を試している意味が…となっていくので、ちゃんとしたプロジェクト設定にするコード量の分岐点は
割と低いのではないのかなと思ったりしますね。