0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Node.js で TypeScript をネイティブ実行する完全ガイド — ts-node 卒業への道【2026年版】

0
Posted at

Node.js で TypeScript をネイティブ実行する完全ガイド — ts-node 卒業への道【2026年版】

この記事の内容は 2026年3月時点 のものです。Node.js のバージョンや仕様は今後変わる可能性があります。最新情報は Node.js 公式ドキュメント を確認してください。


はじめに — ts-node、そろそろ卒業できるかもしれない

ちょっとしたスクリプトを TypeScript で書きたいだけなのに、package.jsondevDependencies が膨らんでいく経験、ありませんか。

{
  "devDependencies": {
    "typescript": "^5.4.0",
    "ts-node": "^10.9.0",
    "@types/node": "^20.0.0"
  }
}

CI でも npm install が必要で、プロジェクトが増えるたびにこのセットが繰り返される...。「TypeScript 使いたいだけなのにな」と思ったこと、一度はあるんじゃないかと思うんです。

実は、Node.js がここ1〜2年でかなり静かに変化していて、ts-node も tsx も入れずに .ts ファイルを直接実行できる ようになってきています。

この記事では、Node.js のネイティブ TypeScript 実行機能を使って開発環境をシンプルにする方法と、知っておくべき落とし穴を丁寧に整理してみます。


前提:使用する Node.js のバージョン確認

まず自分の環境を確認してください。

node --version

この機能が使えるのは Node.js v22.7.0 以降 です。それ未満の場合は、volta や nvm などのバージョン管理ツールでアップグレードしておくことをおすすめします。

# volta を使う場合
volta install node@22

# nvm を使う場合
nvm install 22
nvm use 22

まず動かしてみる — 最速 Hello World

難しい話は後にして、まず動かしてみましょう。

以下の TypeScript ファイルを用意します。

// hello.ts
const greet = (name: string): string => {
  return `Hello, ${name}!`;
};

console.log(greet("TypeScript"));

実行してみます。

# Node.js v22.7.0 〜 v23.5.x の場合
node --experimental-strip-types hello.ts

# Node.js v23.6.0 以降の場合(フラグ不要)
node hello.ts
Hello, TypeScript!

tsc も ts-node も何もインストールしていません。それでも動きます。

これが Node.js のネイティブ TypeScript 実行 です。シンプルでしょう。


バージョン別早見表

自分の環境に合ったコマンドを選ぶために、バージョン別の対応状況を整理しておきます。

Node.js バージョン TS ネイティブ実行 必要なフラグ
v22.7.0 〜 v22.17.x 実験的サポート --experimental-strip-types
v22.18.0 以降(予定) 安定サポート 不要(予定)
v23.0.0 〜 v23.5.x 実験的サポート --experimental-strip-types
v23.6.0 以降 安定サポート 不要
v24.x 以降 完全サポート 不要

現在(2026年3月時点)は LTS の v22 系と最新の v24 系が主流です。LTS を使っているなら v22.18 以降か v24 にアップグレードすると、フラグなしで動かせます。


「型チェックしない」ってどういう意味?

ここが一番誤解されやすいポイントです。

Node.js のネイティブ実行は「 型を剥がす(strip types) 」だけです。型チェックはしません。

どういうことかというと、TypeScript の型情報(: string とか as const とか)を全部取り除いて、残った JavaScript として実行するというイメージです。

// TypeScript で書いたコード
const add = (a: number, b: number): number => {
  return a + b;
};

上記が内部的には以下のように変換されて実行されます。

// 型情報が剥がされた状態(内部処理のイメージ)
const add = (a, b) => {
  return a + b;
};

ここで重要な点があります。

型が間違っていても、実行時エラーにはなりません。

// TypeScript 的には型エラーだが、実行は通る
const add = (a: number, b: number): number => {
  return a + b;
};

console.log(add("hello" as any, "world" as any)); // "helloworld" と出力される

「型チェックなしで大丈夫なの?」という不安はもっともです。そこで 役割分担 を意識するのが大事で、こういう感じで考えるといいかもしれません。

役割 担当
型チェック(リアルタイム) VSCode などのエディタ
型チェック(CIなど) tsc --noEmit
実行 node(型を剥がしてJSとして動かす)

型の正確性はエディタと tsc に任せて、Node.js はシンプルに実行だけする。この分業がこの機能の設計思想です。

CI/CD での注意点: ネイティブ実行だけでは型チェックが走りません。本番環境やチーム開発では、必ず tsc --noEmit をCIに組み込んでください。


ts-node / tsx / tsc+node との比較

「じゃあ従来のツールと何が違うの?」という比較をしておきます。

ツール 型チェック 実行速度 外部インストール enum対応 向いているシーン
node(ネイティブ) なし ⚡ 高速 不要 要フラグ スクリプト、CLIツール、試作
ts-node あり 🐢 やや遅め 必要 完全対応 型チェックしながら実行
tsx なし(esbuild) ⚡ 高速 必要 対応 watch mode、高速な開発ループ
tsc + node あり 🔨 2ステップ 必要 完全対応 本番ビルド、CI/CD

ネイティブ実行が向いているのは、こういうシーンです。

  • ちょっとしたスクリプトを書きたいとき
  • 依存関係を最小限にしたいとき(コンテナや Serverless Functions など)
  • TypeScript を試してみたいとき

逆に、enum を多用しているプロジェクトや、型チェックも含めて厳密に実行確認したい場合は ts-node や tsx を選ぶ方が安心です。


落とし穴と回避策

落とし穴 1:enum が動かない

これは一番ハマりやすいポイントです。

// ❌ これは strip-only mode では動かない
enum Direction {
  Up,
  Down,
  Left,
  Right,
}

実行すると次のエラーが出ます。

TypeError: TypeScript enum is not supported in strip-only mode

解決策 A: --experimental-transform-types を追加する

node --experimental-strip-types --experimental-transform-types app.ts

ただしこのフラグも experimental です。本番用途ではなく、開発・試作段階での利用を推奨します。

解決策 B: as const で代替する(推奨)

これが現代的な書き方で、enum を使わずに同等のことができます。

// ✅ as const を使ったモダンな書き方
const Direction = {
  Up: "Up",
  Down: "Down",
  Left: "Left",
  Right: "Right",
} as const;

// 型として使う
type Direction = typeof Direction[keyof typeof Direction];

// 使用例
const move = (dir: Direction): void => {
  console.log(`Moving ${dir}`);
};

move(Direction.Up); // "Moving Up"

as const で定義すると、Direction.Up などが文字列リテラル型として推論されます。enum と似た使い勝手で、型安全性も担保できます。TypeScript 公式でも enum より as const が推奨される傾向にあります。

落とし穴 2:type: "module" との共存

package.json"type": "module" がある場合、.ts ファイルは ESモジュールとして扱われます。

{
  "type": "module"
}

この場合、require() は使えません。import/export 構文を使うように統一してください。

// ✅ ESモジュール構文
import { readFileSync } from "fs";
export const readConfig = () => { /* ... */ };

落とし穴 3:パスの .js 拡張子問題

TypeScript の import 文でファイルパスを指定するとき、.ts ではなく .js で書くルールがあります。

// ✅ TypeScript の慣習では .js を書く
import { helper } from "./utils.js";

ネイティブ実行でもこのルールは同様です。Node.js がモジュール解決の際に .ts.js の変換を内部でやってくれます。


package.json で快適な開発環境を作る

よく使う設定を package.jsonscripts に登録しておくと便利です。

{
  "scripts": {
    "start": "node --experimental-strip-types src/index.ts",
    "dev": "node --watch --experimental-strip-types src/index.ts",
    "typecheck": "tsc --noEmit",
    "build": "tsc"
  }
}

--watch フラグについて少し説明します。

# ファイル変更を監視して自動再起動(Node.js v22以降で安定)
node --watch --experimental-strip-types src/index.ts

これで nodemon を入れなくてもホットリロードができます。依存関係をまた一つ減らせます。

開発フローのイメージはこうなります。

開発中 → node --watch(変更のたびに自動再起動)
         + VSCode で型チェックをリアルタイム確認

CI/CD → npm run typecheck(tsc --noEmit で型エラーを検出)
       → npm run build(本番用に tsc でビルド)

本番実行 → node dist/index.js(TypeScript 不使用、純粋なJSを実行)

まとめ

Node.js v22.7 以降から使えるネイティブ TypeScript 実行、使えるシーンは意外と多いんじゃないかと思います。

  • インストール不要(ts-node / tsx なしで動く)
  • 高速(esbuild などのビルドプロセスをスキップ)
  • シンプル(フラグ一つ、またはフラグなしで動く)

もちろん、型チェックは自前でやる必要があります。「型を剥がすだけ」という設計なので、それは覚えておいてほしい点です。でも、型チェックはエディタとCIに任せて、実行はシンプルに Node.js でやるという分業は、ツールの責任範囲が明確でわかりやすいとも言えます。

enum を多用している既存プロジェクトには向きませんが、新しいスクリプトや小規模ツールから試してみると、「これで十分だった」と気づくシーンが結構あるかもしれません。

v22.18.0 ではフラグなしで動くようになる予定です。Node.js がこういう方向に進んでいるのは、TypeScript を使いたい開発者にとって良い流れだと感じています。


参考リンク

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?