JS に戻れなくなった僕の TypeScript 習慣
〜 型安全性と開発体験を変えた、 僕の日々の “小さなルール” 〜
「なんでこんな初歩的なバグで、数時間も溶かしてるんだろう」
JavaScript だけで書いていた頃、
デバッグ中の僕は、よくそんな自己嫌悪に陥っていました。
📍 `undefined` に対してメソッドを呼んで落ちる
📍 オブジェクトのプロパティ名を一文字だけ間違えている
📍 API レスポンスの形が変わったのに気づかず、本番で壊れる
どれも「ちゃんと見れば分かる」ミスです。
でも、人間は完璧じゃないので、どうしても抜け落ちてしまう。
そのたびに、
「もっとちゃんと気をつけていれば…」
と、自分を責めていました。
そんな僕の開発体験を変えてくれたのが、TypeScript でした。
この記事では、JS に戻れなくなった僕が、
📌 なぜ TypeScript を手放せなくなったのか
📌 日々の開発で意識している「TypeScript 習慣」
📌 型安全性がもたらす、心の余裕とリファクタリング体験
を、僕自身のプロジェクトでの実感をもとにお話しします。
「型って難しそう」「自分にはまだ早いかも」と感じている人にこそ、
“習慣” レベルの小さな工夫として読んでもらえたらうれしいです。
JS だけで書いていた頃の 「見えない不安」
TypeScript に出会う前の僕は、
フロントエンドもバックエンドも、
ほとんど JavaScript で書いていました。
当時の悩みを一言でまとめると、
「コードを書いていて、どこかずっと不安」
という感覚です。
具体的には、こんな状態でした。
📍 関数の引数が「何でも入ってきそう」に見える
📍 戻り値の形が、自分でも一発で思い出せない
📍 どこまでが「想定内の入力」なのか、コードから読み取れない
📍 チーム開発だと、他人のコードを触るのが怖い
もちろん、JSDoc を書いたり、コメントで説明したり、
テストコードを増やしたりといった工夫はしていました。
それでも、
📍 仕様変更が入るたびに
「あそこも、ここも壊れていないかな」と胃がキリキリする
📍 リファクタリングのたびに、
「動いたらラッキー」くらいの気持ちでデプロイする
そんな「見えない不安」と、いつも隣り合わせでした。
TypeScript との出会いで変わった 「開発体験」
そんな僕が TypeScript に本格的に触れ始めたのは、
個人開発のフロントエンドを Next.js で書き直したときでした。
最初は正直、
📍 型エラーがたくさん出てうんざりする
📍 `any` を使えば一瞬で通るのは分かっている
📍 「こんなに厳しくしなくても…」と感じる
という状態でした。
でも、数週間、本気で TypeScript と付き合ってみると、
少しずつ、開発体験の「質」が変わっていきました。
特に大きかったのは、この 3 つです。
1️⃣ エディタが「未来の不具合」を先に教えてくれる安心感
2️⃣ 型を通じて、仕様の「抜け」を洗い出せるようになったこと
3️⃣ リファクタリングが「怖い作業」ではなく、「楽しい改善」になったこと
1️⃣ エディタが 「未来の不具合」 を先に教えてくれる
TypeScript をちゃんと設定しておくと、
コンパイル前に、エディタが危険な箇所を真っ赤に教えてくれます。
☑ 存在しないプロパティにアクセスしている
☑ `undefined` かもしれない値にメソッドを呼んでいる
☑ 戻り値を勘違いしている
こういう「動かしてから気づくバグ」が、
書いている瞬間に浮かび上がるようになりました。
もちろん、TypeScript ですべてのバグが防げるわけではありません。
それでも、
「これは事前に防げたはずのバグだった」
という後悔の数は、明らかに減りました。
2️⃣ 型を通じて 「仕様の穴」 を見つけられる
TypeScript を使うようになってから、
僕は「型定義を書く = 未来の自分との契約を書く」
感覚を持つようになりました。
たとえば、API のレスポンス型を定義するとき。
📍 このフィールドは必須なのか? それとも任意なのか?
📍 空文字列と `null` は、意味が違うのか?
📍 列挙的な値は、string ではなく union 型にできないか?
など、「仕様として決めきれていない部分」が、
型を書くときに浮き彫りになります。
これは、ただ JS で実装しているだけでは、
なかなか意識しにくいポイントです。
TypeScript は、
「まあ、このへんはゆるくしておこう」
と流しがちな部分に、
「本当にそれでいいの?」と問いを投げてくる存在でもあります。
3️⃣ リファクタリングが 「怖くなくなる」
TypeScript を導入して一番うれしかったのは、
リファクタリングへの心理的なハードルが下がったことです。
📍 関数名やプロパティ名を変えたとき
📍 オブジェクトの構造を整理し直したとき
📍 コンポーネントの props をリファクタリングしたとき
型チェッカーが、
「まだ直しきれてない場所がここにあるよ」
と、機械的に教えてくれる。
そのおかげで、
「全部追い切れているか、自分の記憶と集中力だけに頼る」
という状態から、少しずつ解放されていきました。
JS に戻れなくなった僕の TypeScript 習慣
ここからは、僕が実際のプロジェクトで続けている
「JS に戻れなくなった TypeScript 習慣」を、具体的に紹介します。
大それたテクニックではなく、
日々の開発を少しずつ楽にするための「小さなルール」です。
【習慣 1】 `strict: true` を前提にする
まず、`tsconfig.json` では、
基本的に `strict: true` をデフォルトにしています。
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}最初はエラーだらけになりますが、
「どこに曖昧さが残っているのか」
「どの変数が `undefined` かもしれないのか」
が一気に見えるようになります。
ポイントは、
📍 一気に直そうとしないで、
優先度の高い箇所から少しずつ直すこと
📍 どうしても間に合わないところは、
一時的に `@ts-expect-error` などで明示的に逃がすこと
です。
「ここはあえて緩くしている」という 意図をコードに残す ことで、
将来の自分やチームメンバーが困らないようにしています。
【習慣 2】 値より先に 「型」 を書く
新しい機能を書くとき、
いきなり実装から入るのではなく、
型のスケッチから始めるようにしています。
例えば、API レスポンスを扱うなら、
type User = {
id: string;
name: string;
email: string;
role: "admin" | "member";
};のように、まず「データの形」を決める。
これをしておくと、
📍 コード補完が強力になる
📍 どの画面で何を表示できるかが、型レベルで伝わる
📍 後から仕様変更が入ったときも、
「型の差分」を見れば影響範囲がわかる
というメリットがあります。
習慣としては、
☑ 「この機能で扱う主要なデータ構造は何か?」を先に書き出す
☑ 可能なら、その型を `domain` や `models` ディレクトリにまとめておく
ことを意識しています。
【習慣3】 `any` を 「最後の避難所」 にする
TypeScript に慣れてくると、
つい `any` を封印したくなります。
僕も、極力 `any` は使わない方針ですが、
「どうしても間に合わせが必要な場面」では、あえて使うこともあります。
大事にしているのは、
📍 `any` を使うときは、コメントで「なぜここだけ許すのか」を書く
📍 後で直す前提なら、`TODO` を残して Issue と紐付ける
という 2 点です。
// TODO: 外部ライブラリの型定義を整えたら、any を外す
const result: any = thirdPartyLib.doSomething();こうしておくと、
📍 「知らないうちに `any` が増殖していた」という事態を防げる
📍 レビュー時に「ここ、本当に `any` でいい?」と会話しやすくなる
という効果があります。
【習慣4】 型を 「ドキュメント」 として読む・書く
以前の僕は、
☑ README
☑ Notion ドキュメント
☑ 口頭の説明
など、コードの外側の情報に頼ることが多かったです。
今は、
「まずは型定義を見れば、だいたい分かる」
という状態を目指して設計しています。
たとえば、
☑ API クライアントの型
☑ コンポーネントの props 型
☑ ドメインモデルの型
などを、読みやすい名前とコメント付きで整える。
type CreateOrderPayload = {
/** ユーザー ID(必須)。 未ログイン状態では投げないこと */
userId: string;
/** カートに入っている商品の ID 一覧 */
itemIds: string[];
/** クーポンコード。ない場合は undefined */
couponCode?: string;
};こうしておくと、
📍 新しく参加したメンバーが、
ドキュメントを読まずにコードから理解できる
📍 自分自身も、しばらく経ってから読み返しても迷わない
という、「未来の自分たち」への投資になります。
【習慣5】 「エラーの型」 まで設計する
TypeScript を使っていると、
成功時の型だけでなく、エラーの型もきちんと設計したくなります。
例えば、
type DomainError =
| { type: "NetworkError"; message: string }
| { type: "ValidationError"; message: string; fields: string[] }
| { type: "UnknownError"; message: string };のようにしておくと、
📍 `switch(error.type)` で網羅性チェックが効く
📍 「想定していないエラー」があれば、コンパイル時に気づける
というメリットがあります。
実務では、
📍 成功と失敗を `Result<T, E>` 的な型で扱う
📍 UI 側で、「エラーの種類ごとにメッセージを変える」
といった場面で、型の恩恵を強く感じます。
TypeScript の型システムは、
「動くかどうか」だけでなく、
「壊れ方」まで設計する助けになってくれるのです。
TypeScript 習慣がくれた 「心の余裕」
ここまで書いてきた習慣は、
どれも特別なものではありません。
📍 `strict: true` を前提にする
📍 値より先に型を書く
📍 `any` の使用理由を明示する
📍 型をドキュメントとして整える
📍 エラーの型まで設計する
これらを続けてきて、一番大きかった変化は、
「コードを書いているときの、心のざわつきが減った」
ことです。
もちろん、今でもバグは出ますし、
すべてを TypeScript だけで防げるとは思っていません。
それでも、
📍 「これは事前に防げたはずのバグだった」という後悔が減った
📍 リファクタリングや仕様変更のたびに怯えなくなった
📍 「動くかどうか」だけでなく、
「きれいに直せるか」を考えられるようになった
という意味で、TypeScript は、
僕の開発体験に “心の余裕” をくれたツールだと感じています。
だから、もう JS だけの世界には戻れません。
それはきっと、
「TypeScript が流行っているから」ではなく、
「僕の毎日の仕事と、心を守ってくれる存在になったから」
なのだと思います。
あわせて読みたい
▼ React / Next.js での設計まで含めて考えたいときに
TypeScript だけでなく、
Next.js のレンダリング戦略も含めて設計したい人向けの記事です。
どこまでをサーバー側に寄せ、どこからをクライアントにするか
——型とアーキテクチャの両方から考えています。
▼ フォームまわりの「事故」を減らしたいときに
バリデーションやエラーハンドリングを含めた
「型安全なフォーム設計」を、
具体的なコードとともに解説した記事です。
TypeScript と Zod を組み合わせて、
「受け取るデータ」と「扱い方」まで型レベルで守る実践です。
▼ TypeScriptを軸に、AI駆動開発のワークフローも整えたいときに
Cursor や LLM と TypeScript を組み合わせて、
個人開発のプロトタイピング速度を上げるための実践記録です。
ツールに任せる部分と、
人間が型や設計で舵を握る部分のバランスを掘り下げています。
ひとりごと
「TypeScript は難しそうだから、自分にはまだ早い」
と思っていた時期が、僕にもありました。
でも今は、
「難しいことを増やすツール」ではなく、
「人間の限界を補ってくれる相棒」だと感じています。
毎日のようにコードを書いていると、
📌 集中力が切れている時間帯
📌 つい雑に書いてしまう瞬間
📌 思った以上に仕様が複雑だった、という場面
が、必ずやってきます。
そんなときに、
型チェッカーやエディタが「未来の自分」を守ってくれる。
TypeScript 習慣は、
単に「型の書き方」を増やすことではなく、
「自分を責めすぎないための、仕組みを持つこと」
でもあるのかもしれません。
もしあなたが今、
JS だけで書いていて、不安やストレスを抱えているなら、
この記事の中から、
一つだけでも「習慣」として真似してもらえたらうれしいです。

いいなと思ったら応援しよう!
最後まで読んでいただき、ありがとうございます。もしこの記事が「役に立った」「心に響いた」と感じたら、珈琲一杯分サポートいただけると嬉しいです。あなたの温かい応援を力に、また次の創作活動に励みます。