はじめに
この記事では、TypeScript学習のための実践的な開発環境の構築手順を紹介します。
学習環境としての構築ですが、実際の開発にも耐え得る環境を構築しているのでぜひ活用してみてください。
本記事ではWSLとVSCodeのセットアップは解説していません。
WSLとVSCodeセットアップ方法はこちら をご覧ください。
この記事でわかること
・WSLとVSCodeを組み合わせた環境でのTypeScriptプロジェクトセットアップ手順
・TypeScriptからJavaScriptへのトランスパイルの流れとその手法
・TypeScriptとJavaScriptの実行方法
・ESLintとPrettierの導入と統合
目次
- npmのインストールとプロジェクト初期化
- TypeScriptのセットアップ
- ts-nodeの導入から学ぶトランスパイルと実行の仕組み
- 継続的トランスパイル
- ESLintとPrettierの導入と統合
- スクリプト設定と便利コマンドの活用
1. nvmのインストールとプロジェクト初期化
TypeScriptを導入する前段階として、nvm
のインストールとnpm
の初期化を実行します。
nvm
はNode Version Manager
の略で、node.js
のバージョン管理を行ないます。
npm
はnode package manager
の略で、nodeプロジェクトの依存関係を管理してくれるパッケージマネージャーです。
TypeScriptは JavaScript のスーパーセットであるため、実行には JavaScript のランタイムであるNode.js
(あるいはブラウザ等)が必要になります。
また、nvm
はあくまでnode.js
のバージョン管理であるため、依存関係は専用のパッケージマネージャーが必要です。
今回はnpm
で依存関係を管理します。
node.js
のパッケージマネージャーはnpm
以外にもありますが、本記事では取り扱いません。
-
nvmのインストール
sudo apt install nvm node -v # node.jsのバージョンが表示される。筆者の環境は v22.12.0 npm -v # npmのバージョンが表示される。筆者の環境は 11.0.0
-
nvm
をインストールすると、npm
も一緒にインストールされます。
-
-
プロジェクト初期化
npm init -y # package.jsonが自動生成される
- 作成された
package.json
の"type"
を"module"
に変更します。type
はモジュールのインポート形式を指定しており、JavaScript modules(ESM
)とCommonJS
があります。 -
CommonJS
はnode.js
が開発された初期の頃の古い規格であり、現在はESM
が主流となっています。CommonJS
を指定する場合、トランスパイル後のJavaScript
のバージョンをES5
(2025/1/18現在の最新はES2022
)という古いバージョンに指定する必要があり、実質的にESM一択と言えます。
- 作成された
2. TypeScriptのセットアップ
TypeScriptでの開発を円滑にできるようにするための様々な準備をします。
このセクションでは、必要な準備を進めながら、なぜそれが必要なのかも理解できるように、解説に加えて適宜”想定通りではない結果”となる操作も実行します。
想定通りではない結果やエラーを通して、設定や操作の必要性を理解しつつ環境を構築していきましょう。
-
TypeScriptのインストール
npm install typescript --save-dev # TypeScriptが開発環境の依存関係としてインストールされる
-
--save-dev
オプションを指定することで、TypeScriptを開発環境専用の依存関係としてインストールします。TypeScriptは開発中にのみ使用され、本番環境ではトランスパイル済みのJavaScriptが実行されるため、この設定が推奨されます。
-
-
ディレクトリ作成
mkdir src dist
- ソースファイル格納先の
src/
ディレクトリと、トランスパイルファイル出力先のdist/
ディレクトリを作成。
- ソースファイル格納先の
-
index.tsの作成
echo "console.log(\"complete npx tsc\");" > src/index.ts # src配下にindex.tsを作成 # トランスパイルして実行するとコンソールに complete npx tsc と表示されるスクリプト
- TypeScriptファイルを作成し、トランスパイルを試す準備を整えます。
-
index.tsのトランスパイル
npx tsc # トランスパイルを実行するコマンド。エラーが発生する
- トランスパイル対象もオプションも指定していないため、tscが何をすればいいのかわからないためエラーが発生します。
- エラーを回避するには、対象を引数で渡すか、後述のtsconfig.jsonの設定が必要になります。
npx tsc src/index.ts # エラーなくindex.tsがトランスパイルされるが、src/配下に出力される
- トランスパイル対象を引数として渡すことで正常にトランスパイルされます。
- トランスパイル出力先として
dist/
を作成していましたが、src
に出力されています。これは、出力先が指定されていないためです。 - 対象と出力先を毎回指定すれば正しくトランスパイルされますが、毎回は面倒です。tsconfig.jsonを作成することでこの問題をまとめて解決できます。
-
index.jsの実行
tsconfig.jsonの作成の前に、せっかくトランスパイルしたので、一度index.js
を実行してみましょう。node src/index.js # complete npx tsc がコンソールに表示される
-
node <ファイルパス>.js
でJavaScriptファイルを実行できます。
-
-
tsconfig.jsonの作成と設定
npx tsc --init # TypeScriptプロジェクトの初期化。tsconfig.jsonが自動生成される
- 自動生成された
tsconfig.json
には、基本的な設定と、コメントアウトされたその他設定が既にに記述されています。 - 好みやチームの事情に応じてカスタマイズすればよいですが、最初はよくわからないと思うので筆者の設定を公開しておきます。必要最低限の設定のみとなっているため、必要に応じて追加や修正をするとよいでしょう。
設定例:
{ "compilerOptions": { "target": "ESNext", "module": "NodeNext", "rootDir": "./src", "outDir": "./dist" }, "include": ["src/**/*"] }
-
compilerOptions
:トランスパイルにかかわる設定を記述するセクション -
target
:トランスパイル結果をESM
とCommonJS
のどちらに準拠するかとそのバージョンを記述する。つまり出力された.js
のESM
orCommonJS
とそのバージョン。ESNext
は最新のESM
を指定。つまりESM latest。 -
module
:トランスパイルにどのモジュールシステムを使用するかを指定。Node.js
のESM
解決方式に合わせたNodeNext
を指定。これも意味的にはESM latest。 -
rootDir
:トランスパイル対象ファイルのルートディレクトリを指定する。なくてもトランスパイルは可能だが、ルートディレクトリを認識できないので、dist/src/<ファイル名>.js
になったりする。 -
outDir
:トランスパイル出力先を指定する。指定しないとトランスパイル元と同じ階層に出力される。 -
include
:トランスパイル対象を指定する。指定しないとtsconfig.json
と同階層以下の.ts
,.js
,.mjs
等のJavaScriptやTypeScript系のあらゆるファイルを対象にしようとする。
- 自動生成された
-
tsconfig.json作成後、もう一度トランスパイルと実行をしてみる
npx tsc # dist/index.js が作成される node dist/index.js # complete npx tsc がコンソールに表示される
- トランスパイル対象もオプションも指定せずにトランスパイルが成功します。
- トランスパイルファイルも
dist/
配下に生成されています。
3. ts-nodeの導入から学ぶトランスパイルと実行の仕組み
このセクションでは、tsc
を実行せずとも直接.ts
を実行できるts-node
を導入し、いくつかの設定変更やエラーを通してトランスパイルと実行についての理解を深めていきます。
環境構築手順というよりは学習目的が強いので、とにかく構築を進めたい人は飛ばしていただいても大丈夫です。
-
トランスパイル操作をせず直接実行できるように ts-node の導入
npm install ts-node --save-dev npx ts-node src/index.ts # TypeError: Unknown file extension ".ts" が発生してエラーとなる
-
npx ts-node <ファイル名>.ts
で直接tsファイルを実行できます。ただし、今回はエラーが発生しています。 - このエラーは
.ts
ファイルをnode.js
が認識できないという意味のエラーです。 -
node src/index.ts
でもまったく同じエラーが発生します。 -
package.jon
のtype
をmodule
からcommonjs
に変更するとエラーが解消されます。次項で試してみましょう。
-
-
package.jsonのtypeをcommonjsに変更し、ts-nodeを試してみる
"keywords": [], "author": "", "license": "ISC", "type": "commonjs", "description": "", "devDependencies": { "globals": "^15.14.0", "ts-node": "^10.9.2", "typescript": "^5.7.3" }
npx ts-node src/index.ts # エラーなく実行できる
-
commonjs
で実行できて、module
(ESM
)で実行できない理由は、ts-node
の実行方式とフックがESM
に対応していないからです。 - 次項では
ts-node
のフックおよび実行方式と、tsc
によるトランスパイルと実行の違いについて解説します。
-
-
ts-nodeのフックとnodeによる実行の仕組み
nodeによる実行とts-nodeによるフックについて解説していきます。
まずは、commonjsにおける実行の仕組みです。commonjsにおける実行の仕組み
node index.js ⇒ require('index.js') ⇒ index.js を実行 ts-node index.ts ⇒ require('index.ts') ⇒ (フックで ts-node がトランスパイル) ⇒ require('index.js' 相当のコード) ⇒ index.js を実行
- フックとは、ある処理が行われる前または後に処理を差し込んで実行する仕組みのことです。
-
ts-node
はcommonjs
のモジュール読み込み処理であるrequire()
の前にトランスパイルするフックを持っています。 -
node.js
が実行のためにrequire()
を実行する際に、ts-node
はそのファイルをトランスパイルします。これにより、.ts
ファイルを指定しても最終的にnode.js
が実行するファイルは.js
となります。(正確にはフックの後の処理はオンザフライと呼ばれる方式で、トランスパイル後のソースを直接node.js
に渡しています。) - トランスパイルしたソースを
node.js
に直接渡しているため、dist/
ディレクトリにファイルが出力されません。気になる方はdist/index.js
を削除して試してみてください。
次に、ESMにおける実行の仕組みを説明します。
ESMにおける実行の仕組み
node index.js ⇒ import index.js ⇒ index.js を実行 ts-node index.ts ⇒ import index.ts ⇒ (フックが適用されないためエラー; Node.js が .ts を認識しない)
-
ts-node
のフックはrequire()
に対するフックであるため、import
には反応しません。 - 結果的に、
node.js
には.ts
がそのまま渡されるため、認識できずにエラーとなります。
-
ESMにおける.tsファイルの直接実行
ESM
において、ts-node
はそのままでは.ts
ファイルを直接実行できません。しかし、ある方法でESM
でもts-node
による直接実行が可能になります。node --loader ts-node/esm src/index.ts # 直接実行が成功する。ただし、ExperimentalWarningが表示される
- この方法なら
.ts
の直接実行が可能です。ただし、警告文が表示されます。これは、このコマンドが実験段階のコマンドであり、正式リリースされたものではないからです。 -
--loader
はnode.js
のモジュール読み込みに使うローダーを指定します。 -
ts-node/esm
はESM
に対応したts-node
のフックをローダーとして提供します。 -
node.js
のローダーとして、ESM
に対応したts-node
のフックを指定することにより、.ts
の直接実行を実現します。
- この方法なら
このように、node.js
のローダー指定とts-node
のフックを組み合わせることで.ts
ファイルを直接実行できます。
しかし、この方式はまだ正式リリースではないため、予期しない動作や将来的に削除されるリスクがあります。
次節では、リスクがなく安定でありながら利便性の高いトランスパイルと実行の方式として、tsc --watch
による継続的トランスパイルの方法を解説します。
(次節に進む前にpackage.json
のtype
をmodule
に戻しておきましょう)
4. 継続的トランスパイル
tsc
によるトランスパイルは確実で安定していますが、ファイルを変更するたびに実行するのは少々手間です。
前節ではts-node
による.ts
の直接実行方法を紹介しましたが、そのままではCommonJS
でしか実行できませんでした。ESM
でも実行する方法も紹介しましたが、実験的機能であり正式採用するにはリスクがあります。
ここでは、トランスパイルを自動かつ継続的に実行する方法を紹介します。
-
ターミナルの分割
ターミナルの右端にある”+”マークをクリックします。
ターミナルが複製され、右端にターミナルを選択するウィンドウが追加されます。- 右端のターミナル名を 右クリック ⇒ 名前の変更 で名前を変更できます。
- ゴミ箱マークをクリックすれば対象のターミナルを終了できます。
-
継続的トランスパイルの起動
npx tsc --watch # 継続的トランスパイルが常駐する
- 継続的トランスパイルを実行する
--watch
オプションでtsc
を常駐します。 - 常駐するため、
--watch
を実行中のターミナルではほかの操作ができません。複製したターミナルで実行することで元のターミナルで操作を継続できます。(コマンド末尾に&
をつけてバックグラウンド実行したり等、ほかにもいろいろ方法はあります。あくまで一例です) -
ctrl + c
で終了できます。
- 継続的トランスパイルを実行する
-
.tsファイルを修正し、自動コンパイルされていることを確認
echo "console.log(\"watch test\");" > src/watch_test.ts # src配下に--watch確認用の.tsファイルを作成 node dist/watch_test.js # watch test
- すぐに自動トランスパイルされ、
dist/watch_test.js
が生成される。 -
.ts
作成後、即座に実行すると間に合わずにError: Cannot find module
となる場合がある。エラーを認識した瞬間にはもうトランスパイルされているはずなので、もう一度実行しよう。
- すぐに自動トランスパイルされ、
5. ESLintとPrettierの導入と統合
前節までの準備で、TypeScriptの実行環境は整いました。
このままでも開発は可能ですが、せっかくなら安全で読みやすいコードを書けるようにさらに環境を整えていきましょう。
このセクションでは、linterとしてTypeScriptの文法や型をチェックし自動修正してくれるESLintと、フォーマットチェックと自動修正をしてくれるPrettierの導入を行ないます。
-
ESLintのインストールと初期化
npm install eslint --save-dev # ESLintのインストール npx eslint --init # ESLintの初期化。いくつかの質問が表示され、回答に合わせて初期化内容が変化する
- 初期化時の質問と回答例:
-
質問:How would you like to use ESLint?
- 回答:To check syntax and find problems
- 補足:eslintを構文チェック以外の種々の問題確認にも適用
- 回答:To check syntax and find problems
-
質問:What type of modules does your project use?
- 回答:JavaScript modules (import/export)
- 補足:モジュールのインポート形式の選択。moduleはESMを指す
- 回答:JavaScript modules (import/export)
-
質問:Which framework does your project use?
- 回答:None of these
- 補足:React、vue.js、プレーンなTypeScriptのいずれかを選択できる。
- 補足:今回は純粋なTypeScriptの環境を構築するのでNone of theseを選択
- 回答:None of these
-
質問:Does your project use TypeScript?
- 回答:Yes
- 補足:TypeScriptの環境なのでyes
- 回答:Yes
-
質問:Where does your code run?
- 回答:Nodeのみ
- 補足:今回は純粋なTypeScript環境で、ブラウザ実行する予定はないためNodeのみとする
- 補足:ブラウザのチェックは外しておく(スペースキーでオンオフ切り替え可能)
- 回答:Nodeのみ
-
質問:The config that you've selected requires the following dependencies:
eslint, globals, @eslint/js, typescript-eslint
? Would you like to install them now? ‣ No / Yes- 回答:Yes
- 補足:依存関係としてeslint, globals, @eslint/js, typescript-eslintをインストールが必要だが、インストールしてよいか聞かれている。インストールが必要なのでyes。
- 回答:Yes
-
質問:Which package manager do you want to use?
- 回答:npm
- 補足:使用しているパッケージマネージャーを選択。今回はnpmを使用しているので、npm
- 回答:npm
-
質問にすべて答えるとeslintの設定ファイルであるeslint.config.mjsが自動生成されます。
- 初期化時の質問と回答例:
-
Prettierの導入
npm install prettier --save-dev # Prettierのインストール touch .prettierrc # 設定ファイルの作成。Prettierの設定ファイルは自動生成されない
- Prettierの設定ファイルは自動生成されないため、手動で作成する必要があります。
.prettierrc
を下記のように修正{ "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all" }
-
"semi": true
:文末にセミコロンを強制 -
"singleQuote": true
:ダブルクオートとシングルクォートのうち、シングルクォートを強制 -
"tabWidth": 2
:インデントを2byteに強制 -
"trailingComma": "all"
:可能なすべての場所にカンマを付けることを強制
-
統合用パッケージの導入
npm install eslint-config-prettier eslint-plugin-prettier --save-dev
-
eslint-config-prettier
はESLint
とPrettier
のルール競合を回避する設定を提供します。ESLint
はフォーマッターの機能もあるため、そのままだとPrettier
と競合が発生します。 -
eslint-plugin-prettier
はESLint
にPrettier
のルールを適用するためのプラグインです。ESLint
のフォーマッター機能をPrettier
に置き換えます。
-
-
ESLintとPrettierの統合設定
ESLint
とPrettier
を統合するために、eslint.config.mjs
の設定を修正します。
ここでは、自動生成されたeslint.config.mjs
の内容と、修正後の内容を載せるのみに留めます。
各設定の意味と意図についてはすべてコメントで補足してありますので、皆さんの理解の助けになれば幸いです。
正直、ここはかなりの鬼門で相当な試行錯誤をしました。公式リファレンスを確認しながら試行錯誤を重ねた結果となるので、最適解であると断言はできないです。より効率的な設定や高機能な設定をご存じの方はコメントで教えていただけると大変助かります。-
自動生成された
eslint.config.mjs
import globals from "globals"; import pluginJs from "@eslint/js"; import tseslint from "typescript-eslint"; /** @type {import('eslint').Linter.Config[]} */ export default [ {files: ["**/*.{js,mjs,cjs,ts}"]}, {languageOptions: { globals: globals.node }}, pluginJs.configs.recommended, ...tseslint.configs.recommended, ];
-
修正後の
eslint.config.mjs
import globals from "globals"; // node用のglobal変数をインポート import eslint from "@eslint/js" // js用のeslint。ベースとなる。 import tseslint from "typescript-eslint"; // TypeScript用のeslint。拡張設定。 import tsParser from "@typescript-eslint/parser"; // TypeScriptのAST解析用のparser。静的解析をサポート。 import prettierConfig from "eslint-config-prettier"; // ESLint のスタイルルールとの競合を無効化するパッケージ。 import prettier from "eslint-plugin-prettier"; // Prettier を ESLint のルールとして利用するためのプラグイン。 /** @type {import('eslint').Linter.Config[]} */ export default [ { ignores: [ "**/dist/**/*", // distディレクトリをlint対象から除外 "**/eslint.config.*", // この設定ファイル自身をlint対象から除外 ], }, { files: ["src/**/*.ts"], // この設定ファイルを適用する対象をsrc配下に限定。この設定をしてもlint対象から除外されるわけではなく、ルール適用外になるだけ。除外はignoresで。 }, ...tseslint.config( // flat configでは設定を配列で列挙する。tseslint.config(...)は配列を返すが、export default [...]で配列形式で記述しているため、入れ子になる。よってスプレッド構文で展開。 eslint.configs.recommended, // tseslintの設定として、eslintの推奨設定を適用。 tseslint.configs.recommended, // tseslintの設定として、tseslintの推奨設定で設定を拡張 tseslint.configs.strict, // eslintの型認識を厳格化。 ), { plugins: { prettier, // Prettier を ESLint のルールとして利用するためのプラグイン。 }, }, { languageOptions: { globals: globals.node, // nodeのデフォルト。nodeのグローバル変数をインポート。 ecmaVersion: 15, // ECMA Scriptのバージョン。tsconfig.jsonと合わせておく。 sourceType: "module", // ESMかCommonJSか。ESMの場合はmodule。 parser: tsParser, // AST解析で使用するparserを設定する。TypeScriptなのでtsParserを指定。 }, rules: { "prettier/prettier": "error", // prettierルールの違反をエラーとして扱う。 ...prettierConfig.rules, // prettierと競合するeslintルールの無効化をする設定の展開。{rules: {...}}のJSON形式であるため、スプレッド構文でrulesまで展開する。 }, }, ];
-
-
ESLintとPrettierのリアルタイム解析の導入
- VSCodeの拡張機能で、
ESLint
を検索し、ESLint
をインストールします。 - VSCodeの拡張機能で、
Prettier
を検索し、Prettier – Code formatter
インストールします。
ESlintとPrettierは、本来はコマンドラインで実行することで解析や修正を行なうパッケージです。
しかし、型やスタイルが正しいかをいちいちコマンドで解析するのは手間です。
拡張機能を導入することで、解析をリアルタイムに反映することができます。 - VSCodeの拡張機能で、
6. スクリプト設定と便利コマンドの活用
package.json
にはscripts
という、スクリプトを記述するセクションがあります。
記述したスクリプトはnpm run <スクリプト名>
で実行することができます。
スクリプトはLinuxのコマンドエイリアスに近いイメージです。
ここでは、筆者が設定したスクリプトを紹介します。
-
package.jsonの設定
"scripts": { "build": "tsc", "start": "tsc && node dist/index.js", "run": "node $npm_config_file", "tsnode": "ts-node $npm_config_file", "tsnode:esm": "node --loader ts-node/esm $npm_config_file", "watch": "tsc --watch", "lint": "eslint .", "lint:fix": "eslint . --fix", "format:check": "prettier src/ --check", "format": "prettier src/ --write" }
-
build
:トランスパイルを実行します。npx tscのほうが短いですが、npm run
の形式で統一することができます -
start
:トランスパイルとindex.js
を実行します。基本的なアプリケーション設計ではメインとなるファイルから必要なモジュールが呼び出されるため、起点となるファイルを指定します。 -
run
:任意の.js
をnode
で実行します。$npm_config_file"
は引数で渡したファイルを読み込みます。- 実行例:
npm run run index.js
⇒complete npx tsc
が出力
- 実行例:
-
tsnode
:ts-node
で指定した.ts
ファイルを実行します。ただし、ts-node
導入済でtype
がcommonjs
の場合のみ成功します。詳しくは3章 ts-nodeの導入から学ぶトランスパイルと実行の仕組み
を参照ください -
tsnode:esm
:ESM
環境で.ts
ファイルを実行します。ただし、実験的機能であるため警告が表示されます。詳しくは3章 ts-nodeの導入から学ぶトランスパイルと実行の仕組み
を参照ください -
watch
:tsc --watch
を常駐します。 -
lint
:ESLint
による静的解析を実行します。ただし、拡張機能でリアルタイム解析されているため、基本的には実行することはありません。 -
lint:fix
:ESLintによる静的解析を実行し、さらに自動修正を実行します。 -
format:check
:Prettier
によるフォーマットチェックを実行します。こちらもリアルタイムで解析されているため、基本的には実行することはありません。 -
format
:Prettier
によるフォーマット自動修正を実行します。
-
-
実行例
- ESLintチェック:
npm run lint # src/watch_test.tsにエラーが検知される。 # watch_test.tsはダブルクォートを使用しているため、シングルクォートのみの設定に違反
- ESlintの自動修正:
npm run lint:fix # src/watch_test.tsが自動修正される # watch_test.tsがシングルクォートに自動修正されている
- ESLintチェック:
まとめ
この記事では、TypeScriptの学習用の開発環境構築方法を解説しました。この環境を基にTypeScriptを活用した開発を楽しんでください!