tsc・tsx・node の役割の違いを説明できる.ts を実行し、型エラーを表示できる状態を作れる@types)がどこから来るのかを説明できる01. 背景で見たとおり、tsc の仕事は型を検査することと型を取り除いた JavaScript を出力することの2つでした。実行はしません。
一方、手元で動かすだけなら tsc を通さない道具もあります。それらは型を検査しません。型注釈を捨てて実行するだけです。
| 道具 | 型を検査する | 実行する | 使いどころ |
|---|---|---|---|
tsc | する | しない | 検査と、配布用 JavaScript の生成 |
tsx | しない | する | 開発中にすぐ動かす。制約が少ない |
node | しない | する | 道具を増やさず動かす。使えない構文がある |
bun / deno | しない/一部する | する | Node 以外の処理系を使う場合 |
実行できたことは、型が正しいことを意味しません。
tsx も node も型注釈を捨てるだけで、内容を見ていません。型エラーがあっても平然と動きます。検査は tsc かエディタが行います。この分担を混同すると、「動いているのにビルドが通らない」という状況の理由が分からなくなります。
tsx・node は型を見ない| 場面 | 使うもの |
|---|---|
| コード例を動かす | npx tsx ファイル名.ts |
| 型エラーを確かめる | npx tsc --noEmit、またはエディタの波線 |
| 出力される JavaScript を見る | npx tsc |
この資料に出てくるコード例は、docs/samples/ に実ファイルとして置いてあります。写経せずに動かしたい場合はこちらを使ってください。
$ cd docs/samples
$ npm i
$ npx tsx 04-null-check.ts # 個別に実行する
$ npx tsc # 全ファイルをまとめて型検査する
ファイル名は 04-null-check.ts のように資料番号が接頭辞になっています。読んでいる章に対応するファイルがすぐ見つかります。
プロジェクトごとに入れます。グローバルに入れると、プロジェクトごとにバージョンを変えられません。
$ npm init -y
$ npm i -D typescript tsx
$ npx tsc --version
Version 7.0.2
tsc は既定で JavaScript を出力します。検査だけしたいときは --noEmit を付けます。
$ npx tsc --noEmit greet.ts
greet.ts:7:21 - error TS2353: Object literal may only specify known properties,
and 'namae' does not exist in type 'User'.
7 console.log(greet({ namae: "Alice" }));
~~~~
Found 1 error in greet.ts:7
エラーがなければ何も出力しません。「何も出ない = 合格」です。
TS7 tsconfig.json がある状態でコマンドラインにファイル名を指定すると、tsconfig.json は読み込まれません。TypeScript 7 はこれをエラーで教えてくれます。
$ npx tsc --noEmit fs-test.ts
error TS5112: tsconfig.json is present but will not be loaded if files are
specified on commandline. Use '--ignoreConfig' to skip this error.
訳: tsconfig.json がありますが、コマンドラインでファイルを指定すると読み込まれません。
このエラーを飛ばすには '--ignoreConfig' を使ってください。
この挙動は TypeScript 5 系でも同じですが、警告が出ません。
5 系では黙って tsconfig.json を無視するため、「設定したはずのオプションが効かない」という形で現れます。プロジェクト全体を検査するときは、ファイル名を指定せず npx tsc だけを実行してください。
tsconfig.json の雛形は次のコマンドで作れます。中身は 03. tsconfig で扱います。
$ npx tsc --init
tsx は型注釈を取り除いて実行します。この資料のコード例はすべて tsx で動きます。
$ npx tsx hello.ts
Hello, TypeScript!
Node型ストリップ 新しい Node は .ts をそのまま実行できます。型注釈を取り除いてから実行する仕組み(type stripping)が入っているためです。
$ node --version
v26.8.1
$ node hello.ts
Hello, TypeScript!
Node の型ストリップは「取り除く」だけで、変換はしません。そのため、JavaScript のコードを生成する必要がある構文は使えません。
// enum-test.ts
enum Color { Red, Green, Blue }
const c: Color = Color.Green;
console.log(Color[c]);
$ node enum-test.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:
TypeScript enum is not supported in strip-only mode
訳: TypeScript の enum は、取り除くだけのモードでは対応していません。
enum は型ではなく実行時のオブジェクトを生成するため、取り除くだけでは成立しません。同じ理由で、クラスのパラメータプロパティも使えません。
// param-prop.ts
class User {
constructor(public name: string) {} // ← public が代入コードを生む
}
$ node param-prop.ts
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]:
TypeScript parameter property is not supported in strip-only mode
訳: TypeScript のパラメータプロパティは、取り除くだけのモードでは対応していません。
| 構文 | node | tsx | 理由 |
|---|---|---|---|
型注釈・interface・type | 使える | 使える | 取り除くだけで済む |
enum | 使えない | 使える | 実行時のオブジェクトを生成する |
| パラメータプロパティ | 使えない | 使える | 代入コードを生成する |
namespace | 使えない | 使える | 同上 |
この制約は、むしろ推奨される書き方に沿っています。
enum と namespace は「JavaScript に何も足さない」という TypeScript の設計方針の例外にあたる機能で、現在は使用を避けるのが一般的です。enum の代わりに何を使うかは 04. 型の基本 で扱います。
Node 以外の処理系も .ts をそのまま実行できます。
$ bun --version
1.4.0
$ bun run hello.ts
Hello, TypeScript!
$ deno --version
deno 1.31.1 (release, x86_64-pc-windows-msvc)
$ deno run hello.ts
Hello, TypeScript!
bun は enum も実行できます(取り除くだけでなく変換するため)。この資料は Node を前提に進めますが、コード例は bun・deno でもそのまま動きます。
JavaScript のライブラリには型がありません。TypeScript から使うには型定義ファイル(.d.ts)が必要です。
| 型定義の在り処 | 説明 | 例 |
|---|---|---|
| ライブラリに同梱 | 何もしなくても型が付く。最近のライブラリはこれが多い | zod、hono |
@types/* | 有志が書いた型定義を別途入れる(DefinitelyTyped) | @types/node、@types/express |
| 存在しない | 自分で書くか、unknown で受ける(10) | 古いライブラリ |
node:fs のような標準モジュールも、型定義は別途入れます。入れないとこうなります。
// fs-test.ts
import { readFileSync } from "node:fs";
const s = readFileSync("package.json", "utf8");
$ npx tsc
fs-test.ts(1,30): error TS2591: Cannot find name 'node:fs'.
Do you need to install type definitions for node?
Try `npm i --save-dev @types/node` and then add 'node' to the types field in your tsconfig.
訳: 'node:fs' という名前が見つかりません。node の型定義を入れる必要がありますか。
`npm i --save-dev @types/node` を実行し、tsconfig の types に 'node' を追加してください。
$ npm i -D @types/node
TS7 @types/node を入れるだけでは足りません。tsconfig.json の types に "node" を書く必要があります。
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"strict": true,
"types": ["node"] // ← TypeScript 7 では必須
}
}
TypeScript 5 系では types を書かなくても通ります。
5 系は node_modules/@types/ 配下を自動で読み込んでいました。7 では明示が必要です。「5 では通っていたコードが 7 で TS2591 になる」のはこれが原因です。エラーメッセージ自身が types への追加を促しているので、指示どおりに直せば解決します。
型定義は開発中にしか使いません(実行時には消えているため)。-D(--save-dev)で入れます。
$ npm i -D typescript tsx @types/node
エディタが型エラーを波線で示すのは、TypeScript の言語サーバ(tsserver)が裏で動いているためです。tsc とは別のプロセスですが、同じ TypeScript のコードを使っています。
「エディタは赤いのに tsc は通る」「その逆」が起きたら、ほぼ確実にバージョンか設定の食い違いです。
| 症状 | 疑うところ |
|---|---|
| エディタだけエラーが出る/出ない | エディタが使っている TypeScript のバージョン。VS Code は既定で同梱版を使う。プロジェクトの版に切り替える |
| 特定のファイルだけ型が付かない | tsconfig.json の include / exclude から漏れている |
| 設定を変えても反映されない | 言語サーバの再起動が必要(VS Code: TypeScript: Restart TS Server) |
| コマンドラインだけエラーが出る | ファイル名を指定して tsconfig.json が無視されている(02.2) |
VS Code のバージョンをプロジェクトのものに合わせます。
.ts を開いた状態で、コマンドパレットから TypeScript: Select TypeScript Version →「Use Workspace Version」を選びます。これをしないと、チーム内で人によってエラーの出方が変わります。
この先の章を読む前に、次の3つが通ることを確かめてください。
$ node --version # LTS 以降であること
$ npx tsx --version # 実行できること
$ npx tsc --noEmit # 検査が動くこと
エディタで .ts を開き、わざと const n: number = "文字列"; と書いて波線が出ることも確認してください。出なければ、この先のコード例で「エラーになるはず」の箇所が確認できません。
まとめ
| 項目 | この章の結論 |
|---|---|
| 検査と実行 | 別の道具が担当する。tsx・node は型を見ずに実行する |
tsc | 検査だけなら --noEmit。ファイル名を指定すると tsconfig.json が無視される |
node の型ストリップ | 取り除くだけ。enum・パラメータプロパティ・namespace は使えない |
| 型定義 | ライブラリ同梱か @types/*。TypeScript 7 では types への明示が要る |
| エディタ | 言語サーバが検査している。バージョンをプロジェクトに合わせる |
次は 03. tsconfig です。この章で作った tsconfig.json の中身を読み解きます。読者が最も詰まる章です。