TypeScriptコードはそのままでは動かない
まず大前提として、ブラウザも Node.js も TypeScript を「そのまま」実行することはできません。理解できるのはあくまで JavaScript だけです。だから、TypeScript で書いた .ts ファイルは、一度 JavaScript の .js ファイルに変換してから実行する必要があります。この「TypeScript → JavaScript への変換」を担っているのが、公式コンパイラである tsc(TypeScript Compiler)です。
この変換は、厳密には「トランスパイル(ある言語から別の言語への変換)」と呼ぶのが正確ですが、公式ドキュメントでも「compile」という言葉が使われていて、現場でも「コンパイルする」「ts を js にコンパイルする」という言い方が普通にされています。ここでは、あなたの感覚に合わせて「コンパイル」と呼んでいきます。
コンパイルの基本的な流れ(最小パターン)
一番シンプルな流れからイメージを固めましょう。
まず、hello.ts というファイルを用意して、次のように書きます。
// hello.ts
console.log("Hello TypeScript");
TypeScriptこの時点では、まだただのテキストファイルです。ブラウザも Node.js も、この .ts を直接は理解できません。ここで tsc の出番です。ターミナルで次のように実行します。
tsc hello.ts
これで、同じフォルダに hello.js というファイルが生成されます。中身は、ブラウザや Node.js が理解できる普通の JavaScript です。あとは、Node.js なら node hello.js、ブラウザなら <script src="hello.js"> のようにして実行できます。
この「.ts を書く → tsc で .js を生成 → .js を実行」という三段階が、TypeScript のコンパイルの基本リズムです。
コンパイルの裏側で何が起きているか
表面的には「tsc を叩いたら js が出てくる」だけですが、その裏側ではもう少し細かい処理が行われています。TypeScript コンパイラは、まずコードを読み込んで構文解析(パース)し、抽象構文木(AST)という内部表現を作ります。これは、コードの構造をコンパイラが理解できる形にしたものです。
次に、その AST をもとに型チェックが行われます。ここで、型注釈や推論された型に基づいて、「この代入は型が合っているか」「この関数の呼び出しは正しいか」といった検査が行われます。エラーがあればコンパイル時に報告され、.js の出力は行われません。エラーがなければ、最後に TypeScript 特有の構文(型注釈など)を取り除き、対応する JavaScript コードを生成します。
つまり、コンパイルは「構文を理解する」「型をチェックする」「TypeScript の部分を削ぎ落として JavaScript を吐き出す」という三段階くらいのイメージで捉えると分かりやすいです。
エラーがあるときのコンパイルの挙動
コンパイルの流れをもう少し実感するために、わざとエラーを含んだコードを考えてみます。
// greet.ts
function greet(person, date) {
console.log(`Hello ${person}, today is ${date.toUpperCase()}!`);
}
greet("Brendan");
TypeScriptこのコードには、型注釈がなく、date に何が来るかも決まっていません。tsc greet.ts を実行すると、TypeScript はこのコードを解析し、「date が暗黙に any になっている」「toUpperCase を呼んでいるが、date が文字列とは限らない」といった問題を検出します。strict 系の設定が有効なら、ここでコンパイルエラーになり、.js は生成されません。
逆に、次のように型をきちんと付けてあげると、コンパイルは通ります。
// greet.ts
function greet(person: string, date: string) {
console.log(`Hello ${person}, today is ${date.toUpperCase()}!`);
}
greet("Brendan", "Friday");
TypeScriptこのように、コンパイルは単に「ts を js に変換する」だけでなく、「実行前に型の整合性をチェックする」という重要な役割も担っています。
プロジェクト全体をコンパイルするときの流れ
単発のファイルなら tsc file.ts で十分ですが、現実のプロジェクトではファイルがたくさんあります。そこで登場するのが tsconfig.json です。プロジェクトのルートに tsconfig.json を置くと、tsc はそれを読み込み、「どのファイルを対象にするか」「どのバージョンの JavaScript を出力するか」「どこに出力するか」といった設定に従ってコンパイルを行います。
たとえば、src フォルダに .ts を置き、dist フォルダに .js を出力したい場合、tsconfig.json を次のようにします。
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"outDir": "dist"
},
"include": ["src"]
}
この状態で単に tsc と打つと、src 以下の TypeScript ファイルがまとめてコンパイルされ、dist に JavaScript が生成されます。毎回ファイル名やオプションをコマンドラインで指定する必要がなくなり、「プロジェクトとしてコンパイルする」感覚になります。
ツールやビルドシステムとの連携の中でも起きていること
最近の開発では、Vite や Webpack などのビルドツールが裏側で TypeScript のコンパイルを肩代わりしてくれることも多いです。たとえば、Webpack なら ts-loader を通じて内部的に tsc の機能(型チェックやトランスパイル)を利用し、最終的に bundle.js のようなひとつのファイルにまとめます。Vite なら、esbuild などを使って TypeScript を自動的に JavaScript に変換してくれます。
ただ、どのツールを使っていても本質は同じで、「TypeScript はそのままでは動かないので、どこかのタイミングで JavaScript に変換されている」という事実は変わりません。その変換を自分の手で tsc でやるのか、ビルドツールに任せるのか、という違いだけです。
初心者がまず掴んでおきたい感覚
コンパイルの流れを、難しい言葉を抜きにしてまとめると、こうなります。
あなたは TypeScript で「型付きのリッチなコード」を書く。tsc がそれを読み取り、「構文は正しいか」「型は矛盾していないか」をチェックする。
問題がなければ、TypeScript 特有の部分を削ぎ落として、きれいな JavaScript に変換する。
ブラウザや Node.js は、その JavaScript だけを見て実行する。
この「ts はあくまで設計図で、実際に動くのは js」という二段構えのイメージが腹に落ちると、tsconfig.json や strict モード、ビルドツールとの連携といった周辺の話も、すべて「最終的に良い js を吐き出すための仕組み」としてつながって見えてきます。
