【第12回】指示の代わりに型で伝える|基本の型注釈(嫁子が教えるTS講座)

今回のゴール

前回(なぜ型が要るのか)で動機の話は済みました。今回は書き方です。全体の流れは講座の目次へ。


嫁子: 今日は覚えることが多く見えるけど、実際に自分で書く量はそんなに多くないよ。

TypeScriptって、賢いところがあってね。書かなくても分かるものは勝手に判断してくれるの。だから「全部の変数に型を書く」みたいな話にはならない。

それより大事なのは、AIが書いた型注釈を読んで、意図が合ってるか判断できることだね。今日はそっち側で読んでいこう。


① 変数につける

変数名: 型 の順で書きます。

const name: string = "牛乳";
const price: number = 250;
const done: boolean = false;

第4回でやった3つがそのまま型の名前になります。ただし上の3行は、全部書かなくて構いません。理由は次で説明します。


② 書かなくていい場所(型推論)

TypeScriptは、代入されたものを見て型を判断します。

const price = 250;      // number だと分かる
const name = "牛乳";     // string だと分かる

price.toUpperCase();
// エラー: プロパティ 'toUpperCase' は型 'number' に存在しません

型を1文字も書いていないのに、間違いはちゃんと指摘されます。これが型推論です。

書く場所 判断
変数(初期値あり) 書かなくていい
関数の引数 必ず書く(推論できない)
関数の戻り値 書かなくてもいいが、書くと意図がはっきりする
空の配列から始めるとき 書く(const items: Item[] = []

嫁子: 初心者向けの記事だと全部に型を書いてる例が多いんだけど、実務ではそこまで書かないよ。

判断の基準は単純で、推論に任せて困らないところは任せるの。かえって読みにくくなるからね。


③ 関数につける

引数と戻り値の2か所です。

function total(price: number, count: number): number {
  return price * count;
}

最後の : number が戻り値の型です。何も返さない関数は void と書きます。

function render(): void {
  console.log("描き直しました");
}

アロー関数でも位置は同じです。

const addTax = (price: number): number => price * 1.1;

戻り値の型を書いておくと、実装の間違いをその場で捕まえられます。「number を返すと宣言したのに文字列を返している」が、書いた瞬間にエラーになるからです。

嫁子: AIに書かせるときは、戻り値の型は書いてもらったほうがいいよ。

中身を書き間違えても、宣言と食い違った瞬間に止まるからね。宣言が、AI自身への検査になるの。


④ 配列とオブジェクト

配列は「中身の型 + []」です。

const names: string[] = ["牛乳", "卵"];
const prices: number[] = [250, 300];

オブジェクトは、形をそのまま書きます。

const item: { name: string; price: number } = {
  name: "牛乳",
  price: 250,
};

見ての通り長くなるので、実際は名前をつけて使い回します。その書き方が次回の typeinterface です。

第5回でやった「オブジェクトの配列」は、組み合わせるとこうなります。

const items: { name: string; done: boolean }[] = [
  { name: "牛乳", done: false },
  { name: "卵", done: true },
];

⑤ 「無いかもしれない」を表す

現実には、値が無いこともあります。その場合は | で並べます。

let selected: string | null = null;
selected = "牛乳";   // これもOK

これはユニオン型といって、「どれか1つ」という意味です。文字列と数値のどちらでもいい、という書き方もできます。

let id: string | number;

オブジェクトの項目で「あってもなくてもいい」ものは ? をつけます。

type Item = {
  name: string;
  memo?: string;   // 無くてもいい
};

? をつけた項目は、使う前に存在を確認しないとエラーになります。これが undefined の事故を防ぐ仕組みです。

if (item.memo) {
  console.log(item.memo.length);
}

嫁子: この「使う前に確認しろ」が、最初はうるさく感じると思う。私も面倒だなと思ったよ。

でもこれ、第5回で見た undefined の事故を、全部前倒しで潰してるの。うるさいんじゃなくて、後で起きる分をここで払ってるんだね。


any — 使うと全部無効になる

型が分からないときに使える any というものがあります。

let data: any = getSomething();
data.whateverYouWant();   // 何を書いてもエラーにならない

any を書いた瞬間、その値についてはチェックが全部止まります。前回やった「実行前に止める」が効かなくなります。

AIは、型が面倒な場面で any を使って切り抜けることがあります。エラーは消えますが、問題は残ったままです。

見つけたら どうするか
any any を使わずに型をつけて」と頼み直す
unknown こちらは安全側。使う前に確認を強制される

嫁子: any は「型チェックを止めてください」という宣言なの。だから見つけたら一度立ち止まってね。

エラーが消えたから解決した、と思っちゃうところが危ないんだよ。消えたのはエラーであって、問題じゃないからね。


つまずきポイント

: は型、= は値です。const price: number = 250 は「price は数値型で、値は250」と読みます。

型の名前は小文字です。string であって String ではありません。大文字のほうも書けてしまいますが、別物なので使いません。

エラーメッセージは長いですが、最初の1行だけ読めば足ります。残りは詳細です。そのままAIに貼れば意味も教えてくれます。


今回のまとめ


次にやること

次回はinterface と typeです。今日長々と書いた { name: string; done: boolean } に名前をつけて、使い回せるようにします。

今回の宿題:第10回で作った買い物メモの items に、型をつけるとしたらどう書くか考えてみてください。答えは次回に出てきます。

嫁子: 考えるだけでいいよ。手が動かなくても、「name は文字列で、done は真偽値だな」と頭の中で言えたら十分だから。

それがそのまま次回の内容になるからね。