型注釈の基礎——値に名前と形を与える
レッスン2:型注釈の基礎——値に名前と形を与える
このレッスンで学ぶこと
- 文字列・数値・真偽値に型注釈を付けられる
- 配列とオブジェクトに型注釈を付けられる
anyがなぜ危険なのかを説明できる- 型注釈を書く場所と、書かなくてよい場所の見極め方を身につける
レッスン1 では、TypeScript が JavaScript に静的な型付けを加えた言語であることと、コンパイルの段階で間違いを教えてくれる仕組みを確認しました。このレッスンでは、実際に手を動かして型注釈を書く方法を学びます。「値に名前を付ける」のが変数だとすれば、「値に形を与える」のが型注釈です。
書き方そのものは、いくつかの記号を覚えれば身につきます。大切なのは記号の暗記よりも、「なぜここに型を書くと安心なのか」という理由のほうです。1 つずつ、具体的なコードで確かめていきましょう。
型注釈の基本形——コロンの後に型を書く
TypeScript で型注釈を書く基本の形は、変数名や引数名の後ろにコロン : を置き、続けて型の名前を書くというものです。まずは、文字列・数値・真偽値という 3 つの基本型から見ていきます。
const userName: string = "森田 彩花";
const userAge: number = 30;
const isInstructor: boolean = true;
userName: string の部分が型注釈です。「userName という変数には文字列だけが入る」という宣言になります。試しに、この変数へ数値を代入するコードに書き換えてコンパイルしてみましょう。
let userName: string = "森田 彩花";
userName = 30;
コンパイルすると、実行される前に次のメッセージが表示されます。
error TS2322: Type 'number' is not assignable to type 'string'.
「数値型は文字列型には代入できません」という指摘です。この時点でまだ 1 行も実行していないのに、間違いが見つかっています。文字列・数値・真偽値の型注釈は、それぞれ string・number・boolean という小文字の名前で書きます。
⚠️ 注意
String・Number・Booleanのように先頭を大文字にした書き方も存在しますが、これらは意味が異なる特殊な型です。値そのものの型を表したいときは、必ず小文字のstring・number・booleanを使ってください。
型注釈と初期値をどちらも書く場合
変数を宣言すると同時に値を入れる場合、型注釈と初期値の両方を書くこともできます。
const price: number = 1980;
const taxRate: number = 0.10;
const total: number = price + price * taxRate;
このコードでは、price と taxRate の両方に number 型の注釈を付けたうえで、初期値も同時に代入しています。型注釈があることで、後からこのコードを読む人は「price には数値以外が入らない」という前提を、コードを実行しなくても読み取れます。これが、コースの背骨である「型は読み手への説明書」という考え方の具体的な現れです。
配列に型を付ける——同じ形の値を並べる
配列に型を付けるときは、要素の型の後ろに角括弧 [] を書きます。
const scores: number[] = [85, 62, 78, 44, 91];
const fruits: string[] = ["りんご", "みかん", "ぶどう"];
number[] は「数値だけが並ぶ配列」という意味です。この配列に文字列を追加しようとすると、コンパイルの段階で止められます。
const scores: number[] = [85, 62, 78];
scores.push("満点");
error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
JavaScript では配列の中に異なる型の値を混ぜても動いてしまいますが、TypeScript では「この配列にはどの型の値が並ぶ想定か」を型注釈で固定できます。要素の型がそろっていることを前提にした処理(合計を計算する、大文字に変換するなど)を書くときに、この制約が事故を防ぎます。
配列の中身がすべて同じ形であるという前提は、forEach や map のような配列のメソッドと組み合わせたときにも効いてきます。number[] とわかっていれば、コールバック関数の引数が数値であることも同時に保証され、要素に対して数値専用の計算をそのまま書けます。
💡 ポイント
number[]のほかにArray<number>という書き方もあり、どちらも同じ意味です。本コースでは読みやすさを優先してnumber[]の書き方に統一します。
オブジェクトに型を付ける——プロパティごとに形を決める
オブジェクトに型注釈を付けるときは、波括弧 {} の中にプロパティ名と型をコロンで並べます。
const user: { name: string; age: number } = {
name: "森田 彩花",
age: 30
};
{ name: string; age: number } の部分が、オブジェクトの型注釈です。「name は文字列、age は数値というプロパティを持つオブジェクト」という形を表しています。プロパティの区切りには、オブジェクトの値を書くときのカンマではなく、セミコロンを使うのが型注釈の慣例です。
このオブジェクトに、型で定義していないプロパティを追加しようとすると、コンパイルの段階で止められます。
const user: { name: string; age: number } = {
name: "森田 彩花",
age: 30
};
user.email = "morita@example.com";
error TS2339: Property 'email' does not exist on type '{ name: string; age: number; }'.
「email というプロパティは、この型には存在しません」という指摘です。レッスン1 で見た「プロパティ名の綴り違い」の事故は、まさにこの仕組みで防がれています。
📝 補足 同じオブジェクトの形を何度も書くのは手間がかかります。この手間を解決する
typeとinterfaceという書き方をレッスン5 で学びます。ここでは、インラインで型を書く基本の形を押さえてください。
💡 ポイント オブジェクトに型を付けておくと、コードを書いているエディタが「このオブジェクトにはどんなプロパティがあるか」を把握できるようになります。プロパティ名を途中まで打つと候補が一覧表示される、といった補完の仕組みは、型注釈があるからこそ正確に働きます。型は実行前のチェックだけでなく、書いている最中の作業も助けてくれます。
any——型チェックを止めてしまう型
TypeScript には any という特殊な型があります。any を付けた値は、以降どんな型チェックも行われなくなります。
let data: any = "森田 彩花";
data = 30;
data = true;
data.foo.bar.baz;
このコードは、何行あってもコンパイルエラーになりません。文字列を入れた変数に数値や真偽値を入れ直しても、存在しないはずの foo.bar.baz にアクセスしても、TypeScript は何も指摘しません。any は「この値の型チェックはしなくてよい」という宣言だからです。any を付けた変数は、事実上、型注釈のない普通の JavaScript の変数と同じ状態に戻ってしまいます。せっかく TypeScript を使っているのに、その恩恵がまったく及ばない領域をコードの中に作ってしまうわけです。
⚠️ 注意
anyは「エラーが出て困ったから、とりあえず付けて黙らせる」という使い方をされがちです。しかしanyを付けた瞬間、そこから先のコードは TypeScript の保護から外れます。中核メッセージのひとつである「anyは逃げ道であって、使うたびに型の恩恵が消える」を、ここで実感として持っておいてください。
any が思わぬ場所まで広がっていく
any の怖さは、1 か所に付けただけでは終わらない点にあります。次のコードを見てください。
function getUserInput(): any {
return document.querySelector("#name");
}
const input = getUserInput();
const nameLength = input.value.length;
getUserInput の戻り値が any なので、それを受け取った input も any になります。さらに input.value.length のように、存在するかもわからないプロパティを連鎖してたどっても、コンパイルエラーは出ません。関数の入り口 1 か所に付けた any が、その値を使うすべてのコードに広がっていく——これが「any の伝染」と呼ばれる現象です。
対策としては、any の代わりに unknown という型を使う方法があります。unknown は any と同じく「何の型かわからない値」を表しますが、中身を使う前に型を確認しないとコンパイルエラーになる、より安全な型です。
function getUserInput(): unknown {
return document.querySelector("#name");
}
const input = getUserInput();
const nameLength = input.value.length;
getUserInput の戻り値を unknown に変えただけで、先ほどは素通りしていた input.value.length が、コンパイルの段階で「unknown 型の値に対してこの操作はできません」という趣旨のエラーになります。unknown は「型がわからないなら、わかるまで自由に使わせない」という姿勢の型です。中身を安全に使うための具体的な絞り込みの書き方は、レッスン3 で扱います。
外部から来るデータ、例えば入力フォームの値や、まだ整備されていない古いコードとの境界にある値は、まず any ではなく unknown で受け取っておき、必要な形であることを確認してから使う、という順序を習慣にしておくと安全です。
オプショナルなプロパティ——値がないかもしれないことを型で表す
オブジェクトの中には、必ず値が入っているプロパティと、入っていないこともあるプロパティが混ざる場合があります。TypeScript では、プロパティ名の後ろに ? を付けることで、「あってもなくてもよいプロパティ」を型で表せます。
const user: { name: string; age: number; email?: string } = {
name: "森田 彩花",
age: 30
// email は書かなくてもエラーにならない
};
user.email = "morita@example.com"; // 後から追加してもよい
email?: string の ? が、オプショナル(省略可能)であることの印です。会員登録フォームのメールアドレスや、プロフィールの自己紹介文のように、「入力される場合とされない場合がある項目」を扱うときによく使う書き方です。
オプショナルなプロパティを読み取るときは、値が入っていない可能性を意識する必要があります。
const user: { name: string; email?: string } = { name: "森田 彩花" };
console.log(user.email.length); // email が undefined の可能性を無視している
このコードは、email に値が入っていない場合を考えずに .length を呼び出そうとしているため、コンパイルの段階で「undefined の可能性があるプロパティに対してこの操作はできません」という趣旨のエラーが出ます。「値がないかもしれない」ことをどう安全に扱うかは、レッスン3 で null や undefined の扱い方として詳しく学びます。
📝 補足 オプショナルなプロパティは、「値を入れなくてもよい」という意味であって、「入れた値が
string以外でもよい」という意味ではありません。値を入れるならstring型に従う必要があります。この区別は、次のレッスンで学ぶユニオン型と合わせて理解すると、より正確につかめます。
型注釈を書く場所、書かなくてよい場所
ここまでで、文字列・数値・真偽値・配列・オブジェクトへの型注釈の書き方を確認しました。最後に、「型注釈をどこに書くべきか」という判断基準を整理します。
書いたほうがよい場所は、関数の引数です。関数は呼び出し元がどんな値を渡してくるかわからないため、TypeScript は引数の型を推測できません。
function greet(name: string) {
console.log(`こんにちは、${name}さん`);
}
name: string がなければ、greet(30) のように数値を渡しても止められません。関数の引数への型注釈は、コースを通して基本の書き方として扱います。詳しくはレッスン4 で扱います。
関数は、呼び出す側と呼び出される側が別の場所(別のファイル、あるいは別の人が書いたコード)にあることが多い部品です。引数に型注釈がないと、「この関数にはどんな値を渡せばよいのか」を、関数の中身を読んでいちいち確認する必要が出てきます。引数への型注釈は、関数の使い方そのものを説明書として提示する役割を持っています。
省略してもよい場合がある場所は、値を代入すると同時に宣言する変数です。
const userName: string = "森田 彩花";
const userName2 = "森田 彩花";
この 2 行は、実はどちらも同じ意味になります。TypeScript は、右辺に書かれた値を見て、型を自動的に判断してくれる仕組みを持っているためです。この仕組みを「型推論」と呼び、次のレッスン3 でしっかり扱います。今の段階では、「型注釈は必ずしも全部書く必要がない」という点だけ頭に入れておいてください。
🔰 初学者の方へ 「どこに型を書けばいいのかわからない」と感じたら、まずは全部の変数に丁寧に型注釈を書いてみて構いません。慣れてきたら、レッスン3 で学ぶ型推論に任せられる場所を減らしていく、という順番で進めても問題ありません。
実習:プロフィール情報に型を付ける
学んだ内容を組み合わせて、プロフィール情報に型を付けてみましょう。次のコードを profile.ts という新しいファイルに書いてください。
const name: string = "森田 彩花";
const age: number = 30;
const isInstructor: boolean = true;
const hobbies: string[] = ["登山", "映画鑑賞", "パン作り"];
const profile: { name: string; age: number; job: string } = {
name: name,
age: age,
job: "Webコーダー"
};
console.log(`名前:${profile.name}`);
console.log(`年齢:${profile.age}歳`);
console.log(`職業:${profile.job}`);
console.log(`講師かどうか:${isInstructor}`);
hobbies.forEach((hobby) => {
console.log(`趣味:${hobby}`);
});
コンパイルして実行すると、これまでの JavaScript のコースで作ったのと同じ内容が表示されますが、今回はすべての値に型という説明書が付いています。試しに profile.job に数値を代入するコードを追加してから、もう一度コンパイルしてみてください。実行する前の段階で、間違いに気づけるはずです。
まとめ
このレッスンでは、以下のことを学びました。
- 型注釈は
変数名: 型の形で書き、文字列はstring、数値はnumber、真偽値はbooleanを使う - 配列は
型[]、オブジェクトは{ プロパティ名: 型; ... }の形で型を付ける anyは型チェックを止めてしまう特殊な型で、一度付けるとその値を使う先にまで影響が広がるanyの代わりに、中身を確認しないと使えないunknownという選択肢がある- 関数の引数には型注釈を書く。値を同時に代入する変数は、型推論に任せられる場合がある
次のレッスンでは、この「型推論」の仕組みをさらに深く見ていきます。let と const で推論のされ方がどう変わるのか、複数の型を表現する「ユニオン型」、そして null や undefined を安全に扱う方法を学びましょう。今回学んだ型注釈の書き方は、そのまま次のレッスンの土台になります。
確認クイズ
このレッスンの理解度をチェックしましょう。