型推論とは何か
型推論(type inference)は、「あなたが書いたコードから、TypeScriptが自動的に型を推測してくれる仕組み」です。
毎回すべての変数や関数に型注釈を書かなくても、初期値や式の形から「これは number だな」「これは string の配列だな」と判断してくれます。
let age = 20; // number と推論される
const name = "Taro"; // string と推論される
const flags = [true]; // boolean[] と推論される
TypeScriptここでは一切 : number や : string と書いていませんが、TypeScriptは内部的に「そういう型として扱う」と決めています。
この「自動で型が付く」おかげで、型安全性を保ちながら、コード量を増やしすぎずに済むわけです。
変数宣言における型推論
初期値から型を決める
もっとも基本的な型推論は、「変数の初期値から型を決める」というものです。
let count = 0; // count: number
const title = "本"; // title: string
let isActive = true; // isActive: boolean
TypeScriptcount に数値リテラル 0 を入れたので、「これは number 型の変数だ」と推論されます。
その結果、あとから文字列を代入しようとするとエラーになります。
count = 1; // OK
// count = "1"; // エラー
TypeScript型注釈を書いたときと同じように、「一度 number と決めたら、ずっと number」として扱われるわけです。
const と let で推論のニュアンスが変わる
const の場合、値が変わらない前提なので、より「狭い」型に推論されることがあります。
const color = "red"; // "red" という文字列リテラル型として扱われることが多い
let color2 = "red"; // string として扱われる
TypeScriptconst color は「一生 ‘red’ のまま」と分かっているので、「string 全体」ではなく「’red’ という値そのもの」として扱える場面があります。
一方 let はあとから変わる可能性があるので、「string 型」として少し広めに見ます。
この「どこまで狭く(具体的に)推論するか」は、TypeScriptのバージョンや設定にもよりますが、感覚として「const はより固く、let は少しゆるく」くらいで捉えておくとよいです。
式や演算からの型推論
計算結果から型を決める
TypeScriptは、単純なリテラルだけでなく、式の形からも型を推論します。
const price = 1000;
const taxRate = 0.1;
const total = price * (1 + taxRate); // total: number
TypeScriptここでは、price も taxRate も number と推論され、その掛け算・足し算の結果である total も number と推論されます。
このあと total = "1000"; のように書こうとすると、やはりエラーになります。
配列リテラルからの推論
配列も同様に、中身から型が推論されます。
const scores = [80, 90, 100]; // number[] と推論
const names = ["Taro", "Hanako"]; // string[] と推論
TypeScript要素がすべて number なら number[]、すべて string なら string[] という具合です。
もし混在させると、より広い型(たとえば (number | string)[])として推論されます。
const mixed = [1, "2"]; // (number | string)[] と推論
TypeScriptこの「混ざり方」も、TypeScriptがちゃんと見ています。
関数と型推論
戻り値の型推論
関数の戻り値も、return 文から推論されます。
function add(a: number, b: number) {
return a + b;
}
TypeScriptここでは戻り値に型注釈を書いていませんが、a と b が number で、その足し算を返しているので、「戻り値は number」と推論されます。
呼び出し側では、const result = add(1, 2); と書いたとき、result は number として扱われます。
ただし、設計上大事な関数や外部に公開する関数では、あえて戻り値の型を明示することが多いです。
function add(a: number, b: number): number {
return a + b;
}
TypeScriptこうしておくと、「この関数は必ず number を返す」という契約がよりはっきりします。
引数の型は推論されないことが多い
関数の引数については、基本的に「型注釈を書く」のが前提です。
function greet(name) {
return `Hello, ${name}`;
}
TypeScriptこのように書くと、name は any とみなされてしまい、型安全性が落ちます。
strict モードではエラーにもなります。
なので、関数の引数には、
function greet(name: string): string {
return `Hello, ${name}`;
}
TypeScriptのように、きちんと型注釈を書くのが基本です。
「変数や戻り値は推論に任せてもいいけれど、引数は自分で型を書く」というのが、実務でもよくあるスタイルです。
型推論に任せていいところ・任せすぎてはいけないところ
任せていいところ
ローカル変数や、一目で型が分かるような単純な初期化には、型推論をどんどん使って構いません。
const message = "Hello"; // string と分かる
let count = 0; // number と分かる
const items = [1, 2, 3]; // number[] と分かる
TypeScriptこういうところにまで毎回 : string や : number[] と書くと、逆にノイズが増えて読みづらくなることもあります。
任せすぎると危ないところ
一方で、次のような場所は、型注釈を書いた方が安全です。
関数の引数・戻り値
外部との境界(APIレスポンス、ライブラリとのやり取りなど)
意味が重要な値(ID、フラグ、設定値など)
たとえば、ユーザー情報を返す関数なら、
type User = {
id: number;
name: string;
};
function getUser(): User {
return { id: 1, name: "Taro" };
}
TypeScriptのように、「User を返す」と明示しておくと、実装を変えたときに型のズレがすぐに検出されます。
ここを推論に任せてしまうと、「たまたま今は合っているけど、将来ズレても気づきにくい」という状態になりがちです。
初心者がまず掴んでおきたい型推論の感覚
型推論は、「TypeScriptがいい感じに型を付けてくれる魔法」ではなく、「あなたが書いた情報から、論理的に型を決めている仕組み」です。
だからこそ、次のような感覚が大事になります。
初期値がはっきりしているローカル変数は、推論に任せてOK。
関数の引数・戻り値や、外部との境界は、自分で型を書く。
「この型であるべき」という意図を伝えたい場所には、あえて型注釈を書く。
このバランスが取れてくると、コードは短く、でも型はしっかり効いている——という気持ちいい状態になります。
