こんにちは、かつコーチです。
前回は、外部ライブラリの型定義(@types)の探し方・使い方について解説しました。
そして今回が、TypeScript基礎編の最終回です。
テーマは「動いているJavaScriptプロジェクトを、どうやってTypeScriptに移行していくか」です。
新規プロジェクトで最初からTypeScriptを使うのは比較的簡単ですが、すでに動いているJavaScriptの資産をTypeScript化するのは、また違った難しさがあります。
私自身、稼働中のJavaScriptプロジェクトをTypeScriptに移行する作業に関わったことがありますが、「全部一気に書き換える」という発想でやろうとして、途中で身動きが取れなくなった経験があります。
今回は、その反省も踏まえた「無理なく移行するための考え方」を共有していきます。
いきなり全部書き換えないという大前提
一括移行がうまくいかない理由
「TypeScriptに移行しよう」と決めたとき、多くの人が最初にやりたくなるのが、すべてのファイルの拡張子を.jsから.tsに一気に変更することです。
しかし、これをやると次のような問題が一気に噴出します。
- 数百〜数千個の型エラーが同時に表示され、どこから手をつければいいか分からなくなる
- 移行作業に集中するあまり、通常の機能開発が長期間止まってしまう
- 途中で挫折し、中途半端な状態のまま放置されてしまう
私が関わったプロジェクトでも、最初は「まず全部.tsにしてから直していこう」という方針で始めましたが、エラーの数に圧倒されてしまい、結局方針を変更することになりました。
段階的移行という考え方
TypeScriptには、JavaScriptと共存させながら少しずつ移行できる仕組みが用意されています。
tsconfig.jsonでallowJsを有効にすると、.jsファイルと.tsファイルを同じプロジェクト内に混在させられます。
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"strict": false
}
}
この設定により、既存の.jsファイルはそのまま動かしつつ、新しく書くファイルや触るファイルだけを.tsに変えていく、という進め方が可能になります。
「移行完了」をゴールにするのではなく、「触るたびに少しずつ型を足していく」ことをゴールにすると、無理なく進められます。
移行の具体的なステップ
ステップ1:緩い設定から始める
移行の初期段階では、tsconfig.jsonのstrictをあえてfalseにしておくのがコツです。
{
"compilerOptions": {
"allowJs": true,
"strict": false,
"noImplicitAny": false
}
}
最初から厳しい設定にすると、既存コードのほとんどがエラーになってしまい、移行そのものが嫌になってしまいます。
まずは「TypeScriptとしてコンパイルが通る」状態を目指し、型の厳格さは後から少しずつ上げていく方針にしましょう。
ステップ2:影響範囲の小さいファイルから着手する
移行するファイルの順番も重要です。
おすすめの優先順位は次の通りです。
- 他のファイルからほとんど参照されていない、独立したユーティリティ関数
- データの形が明確な「型」を定義しやすいファイル(APIレスポンスの型、定数など)
- 多くのファイルから参照される共通処理・共通コンポーネント
いきなり中心的な共通処理から手をつけると、影響範囲が広すぎて動作確認が大変になります。
小さく安全なファイルから始めて、TypeScriptに慣れながら型を書くコツを掴んでいくのが、遠回りに見えて実は一番早い進め方です。
ステップ3:型定義を先に整備する
JavaScriptのコードをTypeScript化する前に、扱っているデータの「型」を先にまとめて定義しておくと、後の作業がスムーズになります。
// types/user.ts
export type User = {
id: number;
name: string;
email: string;
role: "admin" | "member";
};
APIのレスポンスや、アプリ内で頻繁に使い回されるデータ構造から型定義を先に整理しておくことで、個々のファイルを.ts化するときに「この型を使えばいい」という状態を作れます。
移行中によくあるつまずきポイント
暗黙の型に頼っていたコードが大量にエラーになる
JavaScriptの時代は、関数の引数に何を渡しても動いていたコードが、TypeScript化した途端に大量のエラーを出すことがあります。
❌ Before:型を意識せず、暗黙のルールで運用されていたコード
// もともとのJavaScript関数(コメントだけでルールを説明していた)
// user引数には { name, age } の形のオブジェクトを渡すこと
function formatUserName(user) {
return `${user.name}さん(${user.age}歳)`;
}
このようなコードをそのまま.ts化すると、userが暗黙的にany型になり、noImplicitAnyを有効にした瞬間にエラーになります。
✅ After:コメントで説明していたルールを型として明示する
type UserForDisplay = {
name: string;
age: number;
};
function formatUserName(user: UserForDisplay): string {
return `${user.name}さん(${user.age}歳)`;
}
これまでコメントやドキュメント、あるいは書いた本人の記憶だけに頼っていた「暗黙のルール」を、型として明文化する作業こそが、TypeScript移行の本質的な価値だと私は感じています。
エラーが大量に出ることは失敗ではなく、「今まで見えていなかった前提条件が可視化された」というポジティブなサインだと捉えると、心理的に楽になります。
外部ライブラリの型不足でつまずく
前回解説した@typesの話も、移行作業では頻繁に登場します。
古いプロジェクトほど、型定義が存在しないライブラリや、バージョンの古いライブラリを使っていることが多いためです。
そうした場合は、前回紹介したdeclare moduleでの応急処置や、影響範囲を絞った独自の型定義ファイルの作成で対応しつつ、無理に完璧を目指さないことが大切です。
移行の目的は「型を100%正確にすること」ではなく、「開発中にミスへ気づきやすくすること」だと割り切ると、判断がぶれにくくなります。
移行を止めないための工夫
CIで進捗を可視化する
移行がどこまで進んでいるかを数値で見えるようにしておくと、チームでのモチベーション維持にもつながります。
{
"scripts": {
"typecheck": "tsc --noEmit"
}
}
tsc --noEmit(ファイルを出力せず型チェックだけ行うコマンド)をCIに組み込み、「型エラーの数が増えたら警告する」といった仕組みを作っておくと、移行の逆戻りを防げます。
完璧を目指さず「今より良い状態」を積み重ねる
最後に、移行作業全体を通して一番伝えたいのは、「一気に完璧な型安全プロジェクトを目指さない」という姿勢です。
anyが残っていても、strictが部分的にしか有効になっていなくても、移行前のJavaScriptだけの状態と比べれば、確実にミスに気づきやすいプロジェクトになっています。
小さな移行を積み重ねていけば、半年後・1年後には見違えるほど型に守られたコードベースになっている、というのが実際に移行を経験した私の実感です。
まとめ
こんにちは、かつコーチです(最後にもう一度)。
TypeScript基礎編、25記事お疲れさまでした。
「TypeScriptとは何か」という一番最初の話から始まり、変数の型注釈、インターフェースとtype、ユニオン型、ジェネリクス、strictモード、そして今回の移行の話まで、盛りだくさんの内容を一緒に駆け抜けてきました。
正直なところ、TypeScriptは最初のうちは「なんでこんなに怒られるんだ」と感じる場面も多い言語です。
私自身もそうでしたし、この連載の中でも何度か自分がつまずいた経験を紹介してきました。
ですが、型という仕組みは、慣れてしまえば「実行する前にミスを教えてくれる、信頼できる相棒」に変わっていきます。
この記事のポイント
- 既存のJavaScriptプロジェクトは、
allowJsを使って段階的にTypeScript化するのが現実的 - 移行初期は
strictを緩めに設定し、後から少しずつ厳しくしていく - 影響範囲の小さいファイルや型定義から着手し、暗黙のルールを型として明文化していく
- 移行の目的は「完璧な型安全」ではなく「ミスに気づきやすいコード」を目指すことだと割り切る
次のステップへ
TypeScript基礎編は、これでひとまず区切りとなります。
ここまで型の書き方、型システムの考え方、そして実践的なつまずきポイントをひと通り学んできたあなたなら、次のステップに進む準備はもう十分整っています。
次は、いよいよ「React編」に進んでいきます。
ここまで積み上げてきた型の知識は、React編でもそのまま活きてきます——というより、TypeScriptを前提に組み立てていくことになるので、この基礎編で苦労した分だけ、React編はきっとスムーズに感じられるはずです。
公開まで、ぜひ楽しみにお待ちください。
改めて、TypeScript基礎編25記事、本当にお疲れさまでした。
