TypeScript / LESSON 05 / TS05

TypeScript:型推論とany

1学習の目的

2基礎解説

ここまで全ての変数に型注釈を書いてきましたが、実は多くは省略できます。TypeScriptは代入された値から型を推測してくれるからです。

書き方推論される型注釈は必要か
const n = 42number不要
const s = "A""A"(リテラル型)不要
let s = "A"string不要
function f(x)暗黙のany必須
✅ 覚えるべき重要ポイント
  1. 変数の型注釈はほとんど省略できるconst price = 128000; と書けば自動的に number になる。冗長な注釈はかえって読みにくい
  2. 関数の引数は省略できないfunction f(x) と書くと「暗黙のany」となり、厳格モードではエラーになる。引数には必ず型を書く
  3. const で文字列を宣言するとリテラル型(その値そのもの)に推論される。let なら string。この違いはTS06で重要になる。
  4. any は存在しないプロパティへのアクセスまで通してしまうv.foo.bar.baz のような明らかな誤りも素通りする。
  5. unknown は「型が分からない」を安全に表す。any と違い、型を確かめるまで何もできないので、確認を強制できる。外部から来るデータに向いている。

現場使用例:型注釈を省いて簡潔に書く、関数の引数には必ず型を書く、外部データは unknown で受けて検証してから使う。「どこに型を書くか」の判断が、読みやすさを左右する

// 型注釈は省略できる(自動で推論される)
const price = 128000;        // number
const productName = "PC";    // string
const items = [1, 2, 3];     // number[]
const obj = { name: "A", price: 100 };  // { name: string; price: number }

// ❌ 関数の引数は省略できない(暗黙のany)
function bad(x) {
  return x * 2;
}
// → Parameter 'x' implicitly has an 'any' type.

// ✅ 引数には型を書く(戻り値は推論に任せてよい)
function good(x: number) {
  return x * 2;   // number と推論される
}

// ⚠ any は何でも通ってしまう
const danger: any = "文字列";
console.log(danger.foo.bar.baz);  // エラーにならない(実行時に落ちる)

// ✅ unknown は確認するまで使えない
const safe: unknown = "文字列";
// console.log(safe.toUpperCase());  → エラー

if (typeof safe === "string") {
  console.log(safe.toUpperCase());  // ここでは string として扱える
}

3基本ドリル(10問)

TS05-D01 型注釈を書かずに価格 128000 を宣言し、1.1倍して四捨五入した結果を出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 140800

ヒント

const price = 128000; だけで number と推論される。

模範解答
const price = 128000;
const taxed = Math.round(price * 1.1);

console.log(taxed);
解説

型注釈が無くても型は付いているprice は number と推論され、文字列を代入しようとすればエラーになる。

TS05-D02 function f(x) { return x * 2; } のように引数の型を省略するとどうなるか。
A. 自動で推論される B. 厳格モードでエラーになる C. number になる D. 問題ない
選択★☆☆無料
期待される結果

解答 == B

ヒント

引数には代入される値が無いので、推論の材料が無い。

模範解答
B
解説

Parameter 'x' implicitly has an 'any' type というエラーになる。関数がどう呼ばれるか分からないため、TypeScriptは推論できない。

TS05-D03 引数だけに型を書き、戻り値は推論に任せて、2つの数値を掛ける関数 multiply を定義し、multiply(6, 7) の結果を出力せよ。 コーディング★☆☆無料
期待される結果

出力 == 42

ヒント

function multiply(a: number, b: number) { return a * b; } と書く。戻り値の型は不要。

模範解答
function multiply(a: number, b: number) {
  return a * b;
}

console.log(multiply(6, 7));
解説

戻り値の型は中身から推論される。ただし複雑な関数では、明示したほうが意図が伝わる場合もある。

TS05-D04 型が不明な値を安全に扱う型を補ってコードを完成させよ。 穴埋め★☆☆無料
コード
const data: ____ = "こんにちは";

if (typeof data === "string") {
  console.log(data.length);
}
期待される結果

出力 == 5

ヒント

any より安全な「型が分からない」を表す型。小文字で7文字。

模範解答
unknown
解説

unknown は確認するまで何もできないtypeof で型を確かめて初めて、その型として扱えるようになる。

TS05-D05 const v: any = "文字"; console.log(v.foo.bar); と書くとどうなるか。
A. 実行前にエラーになる B. エラーにならず、実行時に落ちる C. undefined が返る D. 型が推論される
選択★☆☆無料
期待される結果

解答 == B

ヒント

any は型チェックを無効にする。

模範解答
B
解説

any は存在しないプロパティの連鎖まで通してしまう。TypeScriptを使っているのに、JavaScriptと同じ実行時エラーで初めて気づくことになる。

TS05-D06 型注釈を書かずに商品オブジェクト(name: "マウス", price: 3200)を宣言し、「マウス:3,200円」の形式で出力せよ。 コーディング★★☆無料
期待される結果

出力 == マウス:3,200円

ヒント

const product = { name: "マウス", price: 3200 }; だけでよい。

模範解答
const product = { name: "マウス", price: 3200 };

console.log(`${product.name}:${product.price.toLocaleString()}円`);
解説

オブジェクトの型も自動で推論される{ name: string; price: number } と書いたのと同じ扱いになるので、product.stock と書けばエラーになる。

TS05-D07 引数に型を付けるコードを完成させよ。 穴埋め★★☆無料
コード
function half(n: ____) {
  return n / 2;
}

console.log(half(84));
期待される結果

出力 == 42

ヒント

数値を表す型。

模範解答
number
解説

引数の型は必須、戻り値の型は任意——これが基本の使い分け。この関数の戻り値は number と正しく推論される。

TS05-D08 const s = "固定";let t = "可変"; で推論される型はどう違うか。
A. どちらも string B. const は "固定" というリテラル型、let は string C. どちらもリテラル型 D. const は any
選択★★☆無料
期待される結果

解答 == B

ヒント

const は再代入できないので、値が変わらないことが確定している。

模範解答
B
解説

const は値が変わらないので、その値そのものが型になる。この性質はTS06のリテラル型で重要な意味を持つ。

TS05-D09 unknown 型の変数に数値 42 を入れ、数値かどうか確認してから2倍にして出力せよ。数値でなければ「数値ではありません」と出力すること。 コーディング★★☆無料
期待される結果

出力 == 84

ヒント

typeof value === "number" で確認する。確認しないと使えない。

模範解答
const value: unknown = 42;

if (typeof value === "number") {
  console.log(value * 2);
} else {
  console.log("数値ではありません");
}
解説

unknown は確認を強制する。この if を書かずに value * 2 と書くとエラーになるので、検証を忘れることがない。

TS05-D10 型注釈を書かずに数値配列 [128000, 3200, 8500] を宣言し、10000円以上の要素だけを取り出して件数を出力せよ。 コーディング★★☆無料
期待される結果

出力 == 1

ヒント

filter を使う。配列も推論されるので型注釈は不要。

模範解答
const prices = [128000, 3200, 8500];
const expensive = prices.filter((p) => p >= 10000);

console.log(expensive.length);
解説

filter の中の引数 p も number と推論される。配列の型が分かっているので、その中身の型も自動的に決まる。

4実践シナリオ(5問)

TS05-S01 【冗長な型注釈を省く】商品配列(ノートPC/128000、マウス/3200)から合計金額を計算し、「合計:131,200円」と出力せよ。型注釈は関数の引数だけに書き、変数には書かないこと コーディング★★☆無料
期待される結果

出力 == 合計:131,200円

ヒント

配列もreduceの結果も推論に任せる。関数を作る場合は引数にだけ型を書く。

模範解答
const items = [
  { name: "ノートPC", price: 128000 },
  { name: "マウス", price: 3200 },
];

const total = items.reduce((sum, i) => sum + i.price, 0);

console.log(`合計:${total.toLocaleString()}円`);
解説

TS03で書いた同じ処理より、はるかに読みやすい。型は全て推論されているので、安全性は変わらないまま記述量だけが減っている。

TS05-S02 【any の危険性】any 型の変数に商品オブジェクトを入れ、存在しないプロパティ stock にアクセスして出力せよ。エラーにならないことを確認すること。 コーディング★★☆無料
期待される結果

出力 == undefined

ヒント

const data: any = { name: "PC", price: 100 }; のあと data.stock を出力する。

模範解答
const data: any = { name: "ノートPC", price: 128000 };

console.log(data.stock);
解説

型注釈を書いているのに、まったく守られていない。もし { name: string; price: number } と正しく型を書いていれば、この行は実行前にエラーになっていた。

TS05-S03 【unknown で安全に扱う】unknown 型の変数に文字列 "128000" を入れ、文字列であることを確認してから数値に変換して1.1倍し、四捨五入して出力せよ。 コーディング★★☆無料
期待される結果

出力 == 140800

ヒント

typeof で確認してから Number() で変換する。

模範解答
const input: unknown = "128000";

if (typeof input === "string") {
  const price = Number(input);
  console.log(Math.round(price * 1.1));
}
解説

外部から来るデータは unknown で受けるのが安全。APIのレスポンスやフォームの入力など、何が来るか分からないものは、確認してから使う。

TS05-S04 【型を確認して分岐】unknown 型の値を受け取り、数値なら「数値:42」、文字列なら「文字列:あ」、それ以外は「不明な型」を返す関数 describe を定義せよ。42・"あ"・true の3通りで試し、3行出力すること。 コーディング★★★無料
期待される結果

出力3行。数値:42 / 文字列:あ / 不明な型

ヒント

引数の型は unknown。typeof で分岐する。戻り値は string。

模範解答
function describe(value: unknown): string {
  if (typeof value === "number") {
    return `数値:${value}`;
  }

  if (typeof value === "string") {
    return `文字列:${value}`;
  }

  return "不明な型";
}

console.log(describe(42));
console.log(describe("あ"));
console.log(describe(true));
解説

if の中では、その型として扱えるtypeof value === "number" を通った先では value は number 型になっている——これを型の絞り込みと呼び、TS09で詳しく扱う。

TS05-S05 【推論を活かした集計】商品配列(ノートPC/128000/5、マウス/3200/42、キーボード/8500/18)から、在庫金額の合計と平均単価(小数第1位)を計算し、次の2行を出力せよ。変数の型注釈は書かないこと
「在庫総額:927,400円」
「平均単価:46,566.7円」
コーディング★★★無料
期待される結果

出力2行が完全一致

ヒント

平均単価は価格の合計÷件数。toFixed(1) では3桁区切りにならないので、toLocaleString に小数の桁数を指定する方法もあるが、ここでは Number(avg.toFixed(1)).toLocaleString() で対応する。

模範解答
const items = [
  { name: "ノートPC", price: 128000, stock: 5 },
  { name: "マウス", price: 3200, stock: 42 },
  { name: "キーボード", price: 8500, stock: 18 },
];

const totalValue = items.reduce((sum, i) => sum + i.price * i.stock, 0);
const avgPrice = items.reduce((sum, i) => sum + i.price, 0) / items.length;

console.log(`在庫総額:${totalValue.toLocaleString()}円`);
console.log(`平均単価:${Number(avgPrice.toFixed(1)).toLocaleString()}円`);
解説

型注釈が1つも無いのに、全ての変数に型が付いているitems の構造から i.price の型まで芋づる式に推論されるので、打ち間違いはやはり検出される。

5仕上げ課題

TS05-FINAL 【外部データの安全な処理】外部から届いたデータを検証しながら処理せよ。

受け取るデータ(unknown 型で宣言すること)
const input: unknown = { name: "ノートPC", price: "128000", qty: 2 };
※ price が文字列で届いている点に注意

処理
input がオブジェクトかどうか確認する(typeof input === "object" && input !== null
② オブジェクトとして扱うため、as { name: string; price: string; qty: number } で型を指定する
③ price を数値に変換する
④ 小計(price × qty)を計算する
⑤ 結果を出力する。オブジェクトでなければ「不正なデータです」と出力

変数の型注釈は、必要な箇所(unknown の宣言)以外は書かないこと。

期待される出力(3行)
商品:ノートPC
単価:128,000円 × 2
小計:256,000円
期待される結果

出力3行が完全一致

ヒント

unknown のままではプロパティにアクセスできない。typeof で確認したあと、as を使って「この型として扱う」と指定する(型アサーション)。price は文字列なので Number() で変換する。

模範解答
const input: unknown = { name: "ノートPC", price: "128000", qty: 2 };

if (typeof input === "object" && input !== null) {
  const data = input as { name: string; price: string; qty: number };

  const price = Number(data.price);
  const subtotal = price * data.qty;

  console.log(`商品:${data.name}`);
  console.log(`単価:${price.toLocaleString()}円 × ${data.qty}`);
  console.log(`小計:${subtotal.toLocaleString()}円`);
} else {
  console.log("不正なデータです");
}
解説

この課題には、実務でそのまま出てくる2つの落とし穴が仕込まれている。

1つ目は price文字列で届いていること。JSONやフォームから来るデータでは頻繁に起きる。"128000" * 2 はJavaScriptでは 256000 になってしまい(数値に変換されるため)気づきにくいが、文字列のまま扱えば "128000128000" のような結果になる場面もある。型として「ここは文字列」と明示し、変換を挟むことで事故を防いでいる。

2つ目は as という書き方だ。これは型アサーションと呼ばれ、「TypeScriptよ、これはこの型だと思ってくれ」と開発者が保証する宣言になる。便利だが、実際にその型である保証はどこにも無い——嘘をつけば実行時に落ちる。any ほどではないが、これも慎重に使うべき道具だ。

だからこそ、typeof でオブジェクトかどうかを先に確認している。何の確認もせずに as だけ書くのは、any を使うのとほとんど変わらない。「確認してから断言する」——この順序が安全性を支えている。

次章ではユニオン型を学ぶ。「文字列または数値」のように、複数の可能性を型として表現できるようになり、as に頼らずに済む場面が増える。