TypeScript基礎講座 #1 はじまりとセットアップ
このシリーズは、JavaScriptはある程度わかるけれどTypeScriptは初めてという方のための入門講座です。全7回で構成されます。
- #1 はじまりとセットアップ ← 今回
- #2 基本の型
- #3 interfaceとtype alias
- #4 Union / Literal / Narrowing
- #5 関数の型
- #6 ジェネリクスを深く
- #7 ユーティリティ型とtsconfig
今回は「なぜTypeScriptなのか」をはっきりさせ、初めてのコードを書いてコンパイルして実行するところまで進みます。
TypeScriptって何ですか? #
TypeScriptは、JavaScriptに型システムを加えた言語です。Microsoftが2012年に作り、今ではJavaScript系プロジェクトの事実上の標準になりました。React/Vue/Node.jsの大規模プロジェクトはほぼすべてTypeScriptで書かれています。
ひと言でまとめると、こうなります。
JavaScript + 型注釈 = TypeScript
JavaScriptで書けるすべてのコードはTypeScriptでも動作します。TypeScriptはその上に「この変数は文字列」「この関数は数値を受け取ってbooleanを返す」といった情報を表せる文法を加えたものです。
なぜ型を明示するのですか? #
JavaScriptは動的型付けの言語なので、変数がどの型なのかはコードを実行してみないとわかりません。小さなプログラムでは大きな問題になりませんが、コードが大きくなると次のようなことが頻繁に起こります。
function getDiscount(price, percent) {
return price * percent / 100;
}
getDiscount(1000, 10); // 100 — 正常
getDiscount('1000', 10); // 1000... あれ? 文字列が来たのに動いてしまう
getDiscount(1000, '10'); // 100 — 動くけど意図通り?
getDiscount(1000); // NaN — percentがundefined
JavaScriptは勝手に型を変換したりundefinedで埋めたりして、とりあえずコードを実行してしまいます。バグはずっと後になってから明るみに出るのです。
TypeScriptで同じ関数に型を明示すると、コードを書いている時点で間違った呼び出しを捕まえてくれます。
function getDiscount(price: number, percent: number): number {
return price * percent / 100;
}
getDiscount(1000, 10); // ✓ OK
getDiscount('1000', 10); // ✗ エラー: '1000'はnumberではありません
getDiscount(1000, '10'); // ✗ エラー
getDiscount(1000); // ✗ エラー: 引数が足りません
エラーがコンパイル時に現れます。実行前にエディタで赤線が引かれ、ビルドも止まります。バグがユーザーに届く前に捕まるのです。
TypeScriptの価値をひと言で #
- エディタの自動補完/推論 — オブジェクトにどんな属性があるか、関数が何を返すかをIDEが正確に教えてくれる
- リファクタリングの安全性 — 関数のシグネチャを変えると、それを呼び出すすべての箇所が赤線で示される
- ドキュメントの役割 — 型がそのまま関数の使い方の説明書になる
- バグの事前防止 —
undefined/nullのようなよくある落とし穴をコンパイラが防いでくれる
小さなトイプロジェクトでは負担になりますが、複数人で触るコードや、時間が経ってから読み返すコードでは、TypeScriptの価値が大きく光ります。
TypeScript vs JavaScript — 実行モデル #
ここで重要な事実があります。ブラウザとNode.jsはTypeScriptを直接実行できません。型注釈のような文法を知らないからです。
なので、私たちが書いた.tsファイルは実行前にJavaScriptに変換(コンパイル/トランスパイル)される必要があります。この変換を行うのがtsc(TypeScript Compiler)やesbuild、swc、Viteのようなビルドツールです。
.ts ファイル (作成)
↓ (コンパイル)
.js ファイル (実行可能)
↓
ブラウザ / Node.js興味深いのは、コンパイル結果の.jsファイルからは型情報がすべて消えているという点です。型は開発時にだけ意味があり、実行時にはただのJavaScriptなのです。だからTypeScriptは「ランタイムの追加コストなしに開発時の安全性を得る」ツールなのです。
最初のセットアップ — もっとも単純な方法 #
セットアップ方法はいくつかありますが、最初は一番単純な2つをおすすめします。
方法1. tscを直接使う(学習用) #
純粋にTypeScript自体を学ぶには最適です。
mkdir ts-learn
cd ts-learn
npm init -y
npm install --save-dev typescript
npx tsc --init各コマンドの役割は次のとおりです。
npm init -y—package.jsonを生成(Node.jsプロジェクトにする)npm install --save-dev typescript— TypeScriptコンパイラをインストールnpx tsc --init— 基本のtsconfig.jsonを生成
tsconfig.jsonはコンパイルオプションをまとめた設定ファイルです。詳しいオプションは#7で扱うので、今は作られたままにしておけば大丈夫です。
方法2. Viteで素早く(実務に近い) #
Web開発の文脈ならViteがコンパイル + 開発サーバーまでまとめて処理してくれて便利です。
npm create vite@latest ts-learn
# Framework: Vanilla
# Variant: TypeScript
cd ts-learn
npm install
npm run devこのシリーズは方法1で進めます。tscで直接コンパイルしてみると、TypeScriptが何をしているかが一番はっきり理解できるからです。後でVite/Webpackに移っても同じ原理です。
最初のコード #
ts-learn/フォルダにindex.tsファイルを作ります。
function greet(name: string): string {
return `こんにちは、${name}さん!`;
}
const message: string = greet('太郎');
console.log(message);TypeScript固有の新しい文法は2か所です。
name: string—nameが文字列型であることを明示(): string— 関数が文字列を返すことを明示const message: string = ...— 変数messageが文字列であることを明示
コンパイルして実行する #
npx tscこのコマンドを実行すると、同じフォルダにindex.jsファイルが生成されます(tsconfig.jsonのデフォルト次第で別の場所になることもあります)。そのファイルを開いてみてください。
function greet(name) {
return "こんにちは、".concat(name, "さん!");
}
var message = greet('太郎');
console.log(message);型注釈(: string)がすべて消えています。そしてES6文法(const、テンプレートリテラル)が少し古いJavaScriptに変換されているのが見えるはずです(古いブラウザサポートのためのデフォルト動作 — tsconfig.jsonのtargetオプションで調整可能、#7で扱います)。
これをNodeで実行してみましょう。
node index.jsこんにちは、太郎さん!わざと間違える — 型エラーを実際に見る #
TypeScriptの価値を体感するには、実際に間違ってみるのが一番早いです。index.tsを次のように書き換えてみてください。
function greet(name: string): string {
return `こんにちは、${name}さん!`;
}
const message: string = greet(42); // 🚫 数値を入れた
console.log(message);npx tscをもう一度実行すると、こうなります。
index.ts:5:33 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
5 const message: string = greet(42);
~~
Found 1 error in index.ts:5TypeScriptが5行目33文字目に問題があると正確に指摘してくれます。.jsファイルも生成されません(デフォルト設定では — オプションで調整可能)。間違ったコードが実行段階まで流れていくのを防いでくれるのです。
VSCodeのようなエディタでは、npx tscを実行するより前に、すでに赤線で即座に表示されます。書いている間にリアルタイムで間違いを教えてくれるわけです。
自動推論 — 型を書かなくても推測してくれる #
すべての変数に型を明示する必要はありません。TypeScriptは賢く推論してくれます。
const age = 30; // age: number に自動推論
const name = '太郎'; // name: string
const isAdmin = true; // isAdmin: boolean
const items = [1, 2, 3]; // items: number[]
age = 'hello'; // 🚫 エラー: numberにstringは入れられない
const age = 30と書くと、TypeScriptが自動でage: numberと推論します。その後age = 'hello'のように別の型を入れようとすると捕まえてくれます。
明示的な型注釈は、主に次のような場合に使います。
- 関数の仮引数と戻り値の型(自動推論が弱い)
- 意図を明確に表す(
const id: string = computeId()のように可読性のため) - 空の変数に型だけ先に宣言(
let user: User | null = null)
これからの記事を進める中で、このバランス感覚は徐々に身についていきます。
watch モード — 毎回コンパイルしないために #
毎回npx tscを打つのが面倒なら、watchモードがあります。
npx tsc --watchファイルを修正するたびに自動でコンパイルされ、すぐに結果を受け取れます。学習中はこのモードをつけておくと便利です。
よくある落とし穴と神話 #
「TypeScriptは難しい」 #
最初の数日は不慣れですが、1〜2週間使えばJavaScriptに戻るときにかえって不安になるくらいになります。自動補完 / リファクタリング / 即時のエラーフィードバックの価値が大きいからです。
「TypeScriptは遅い」 #
実行はJavaScriptと同じです(ランタイムコストはゼロ)。コンパイル時間は追加されますが、incrementalビルドでほとんど体感しません。
「anyを使えばいいんじゃない?」 #
anyは型システムを事実上オフにする脱出口です。使いすぎるとTypeScriptの価値を失います。たまに必要なときだけ使い、普段はもっと正確な型を探すのがよいでしょう(#4のunion型など)。
「JSDocでも似たような効果が出せる」 #
そのとおりです。JSDocコメントで型を一部表現できます。ただし表現力とツールサポートではTypeScriptが圧倒的に強力です。
まとめ #
今回扱ったことを整理します。
- TypeScript = JavaScript + 型注釈
- コンパイル時に型エラーを捕まえ、実行時はただのJavaScript
npx tscでコンパイル、watchモードで自動再コンパイル- 型注釈は関数のシグネチャに優先して明示、変数は推論に任せることが多い
- 自分で間違ったコードを書いてみてコンパイルエラーを受け取るのが最良の学習法
次回「TypeScript基礎講座 #2 基本の型」では、string/number/booleanから始めてarray、tuple、object、enumまで — 毎日使うことになる基本的な型を一つずつ見ていきます。