5分で始めるクイックスタート:.jsから.tsへの移行
JavaScriptは非常に柔軟な言語ですが、その自由さには大きな代償が伴うことがよくあります。プロパティ名のたった一つのタイポ(打ち間違い)が、悪名高い TypeError: cannot read property 'x' of undefined を引き起こし、アプリケーション全体をクラッシュさせてしまうのです。2023年のState of JS調査によると、現在70%以上の開発者が、まさにこの問題を解決するためにTypeScriptを使用しています。TypeScriptは静的型付けという層を追加することで、ロジックに対するセーフティネットのような役割を果たします。
私が初めて移行した週のことを覚えています。半分以上の時間をコンパイラエラーとの戦いに費やし、フラストレーションを感じていました。しかし、あることに気づいたのです。コンパイラが出すエラーはすべて、自分が見逃していた潜在的なバグだったのです。本番サーバーがクラッシュして午前2時にアラートを受け取るより、エディタ上で間違いを修正する方がはるかにマシです。
Node.jsがインストールされていれば、開始するのは簡単です。ターミナルで次のコマンドを実行してコンパイラをインストールします:
npm install -g typescript
app.ts という名前のファイルを作成します。.ts という拡張子は、これが単なるJavaScriptではないことを示します。以下のスニペットを貼り付けてください:
let username: string = "itfromzero";
let age: number = 25;
function greet(name: string): string {
return `こんにちは、${name}さん`;
}
console.log(greet(username));
これをブラウザで実行可能なコードに変換するには、コンパイラを実行します:
tsc app.ts
ツールによって即座に app.js ファイルが生成されます。もし greet 関数に数値を渡そうとすると、TypeScriptはファイルを保存する前にその行を赤くハイライトします。この即座のフィードバックループこそが、モダンなチームがTypeScriptなしでの開発を拒む理由です。
エッセンシャル:TypeScriptの考え方
静的型付けは、単にファイルに文字を増やすことではありません。ソフトウェアの異なる部分の間に「正式な契約」を作成することです。私の経験上、この考え方の転換は、実際の構文を学ぶことよりも重要です。
型推論:静かな助っ人
常に明示的に型を書く必要はありません。TypeScriptは驚くほど観察眼が鋭いです。let count = 10; と書けば、エンジンは自動的に number 型を割り当てます。明示的に型を定義する必要があるのは、ロジックが複雑になったときや、空の変数を初期化するときだけです。
- string: すべてのテキストデータに使用します。
- number: 整数と浮動小数点の両方を扱います。
- boolean: シンプルな true または false の切り替えです。
- Array:
number[]またはArray<string>のように記述します。
インターフェース:データの形状を定義する
インターフェースは、クリーンなTypeScriptコードの基本です。オブジェクトがどのような見た目であるべきかを正確に定義できます。正体不明のオブジェクトを使い回すのはやめて、明確な構造を定義しましょう。
interface User {
id: number;
username: string;
email: string;
isAdmin?: boolean; // '?' はこれがオプション(任意)であることを意味します
}
const newUser: User = {
id: 1,
username: "dev_pro",
email: "[email protected]"
};
もし誤って user.email ではなく user.mail と入力してしまっても、エディタがそれを検知します。これにより、フロントエンドはあるフィールド名を期待しているのに、APIが別の名前を返してくるといった、煩わしいバグを排除できます。
混乱のない柔軟性
基本をマスターすると、データが完全には予測できないシナリオに遭遇することでしょう。TypeScriptは、安全性を犠牲にすることなく、これらを優雅に処理します。
Union型と型ガード
現実世界のデータは雑多です。IDは、レガシーなデータベースからの数値 101 かもしれませんし、新しいサービスからの文字列 "uuid-abcd" かもしれません。Union型を使えば、その両方を扱うことができます。
function printId(id: number | string) {
if (typeof id === "string") {
// TypeScriptはここで 'id' が文字列であることを認識します
console.log("IDは文字列です: " + id.toUpperCase());
} else {
// ここでは 'id' は確実に数値です
console.log("IDは数値です: " + id.toFixed(2));
}
}
printId(101); // 有効
printId("abc-123"); // これも有効
上記の if チェックは「型ガード(Type Guard)」と呼ばれます。これによりコンパイラが型を絞り込めるため、toUpperCase() のような型固有のメソッドを安全に使用できるようになります。
ジェネリクス:再利用可能なロジック
ジェネリクスは、型のための変数のようなものです。関数を一度書くだけで、型安全性を保ったまま文字列、数値、あるいはカスタムオブジェクトに対して利用できます。
function wrapInArray<T>(item: T): T[] {
return [item];
}
const numberArray = wrapInArray(5); // 結果は number[] 型
const stringArray = wrapInArray("TS"); // 結果は string[] 型
<T> はプレースホルダーです。数値を渡すと T は number になります。これにより、良いTypeScriptを書くための鉄則である any 型の使用を避けることができます。
現場で学んだ教訓
大規模なプロジェクトをTypeScriptに移行するには、単に構文を知っているだけでは不十分です。コードベースを健全に保つために私が使用している3つの戦略を紹介します。
1. ‘any’ の罠を避ける
急いでいるときは any を使いたくなりますが、やってはいけません。any を使うことは、実質的にTypeScriptをオフにし、JavaScriptの無法地帯に戻ることを意味します。もし本当に型がまだ分からない場合は、unknown を使いましょう。変数を使用する前に型チェックを強制されるため、アプリの安全性が保たれます。
2. 早めに厳格モードを有効にする
すべてのプロジェクトには tsconfig.json ファイルが必要です。tsc --init を実行して生成しましょう。その中で、"strict": true が有効になっていることを確認してください。コンパイラの指摘は厳しくなりますが、JavaScriptアプリを悩ませる最も一般的な null ポインタ例外を防ぐことができます。
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"strict": true,
"skipLibCheck": true
}
}
3. 明確さのために型エイリアスを使用する
もし string | number | undefined と何度も書いているなら、それはメンテナンスの悪夢を作っているようなものです。代わりに型エイリアス(Type Alias)を作成しましょう。関数のシグネチャが一目で理解しやすくなります。
type UserID = string | number;
function fetchUser(id: UserID) {
// ずっとスッキリします!
}
TypeScriptは最初は手間が増えるように感じるかもしれませんが、リファクタリングの際にその真価を発揮します。一つのプロパティ名を変更しただけで、コンパイラが壊れた15箇所のファイルを即座に教えてくれるとき、TypeScriptが最高のチームメイトであることを実感するはずです。

