TypeScript の変数と型推論
TypeScript の最も強力な機能の一つが「型推論」です。型注釈を至る所に記述する必要はなく、コンパイラが自動的に推論してくれます。ただし、コンパイラが正しく解釈できるコードを書くためには、この推論のルールを理解しておく必要があります。
1. 変数の宣言:let、const、および var
TypeScript は、JavaScript における変数の宣言方法 3 つすべてを完全にサポートしていますが、let および const を使用することを推奨します:
(1) let — 再代入可能な変数
TYPESCRIPT
let count = 0;
count = 1; // ✅ let Variables can be reassigned
(2) const — 再代入できない定数
TYPESCRIPT
const MAX_SIZE = 100;
MAX_SIZE = 200; // ❌ Error:Cannot be assigned to a constant
(3) var — 関数スコープ(推奨されません)
TYPESCRIPT
var x = 1; // ⚠️ Available but not recommended——var There are issues with variable hoisting and scope leakage.
📌 重要ポイント: 常に
let と const を使用し、var は使用しないでください。もし const を使用できる場合は、代わりに const を使用してください(値が変更されない場合)。let は、値を再割り当てする必要がある場合にのみ使用してください。これは JavaScript のベストプラクティスと完全に一致しています。
(4) 型アノテーションと型推論
TYPESCRIPT
// Type Annotations——Tell me TypeScript What is the type of the variable?
let age: number = 25;
// Type Inference——TypeScript Automatically determine the type based on the initial value
let name = "Charlie"; // TypeScript Inferred as string
let active = true; // TypeScript Inferred as boolean
どちらのアプローチでも結果は同じになりますが、初期値が指定されている場合は、型推論が機能するように、アノテーションを省略することをお勧めします。
2. 型推論の完全な規則
型推論は「当て推量」ではなく、明確なルールに基づいています。これらのルールを理解して初めて、コンパイラの挙動を予測することができるのです。
(1) 規則 1:初期値の推定
変数に初期値が設定されている場合、TypeScriptはその初期値に基づいて型を推論します:
TYPESCRIPT
let message = "hello"; // Inferred as string
let count = 42; // Inferred as number
let flag = true; // Inferred as boolean
let items = [1, 2, 3]; // Inferred as number[]
(2) 規則 2:const リテラルに対する型推論
これは、TypeScriptの型推論において最も微妙な点の一つです。つまり、letとconstの推論結果が異なるのです:
TYPESCRIPT
// let Inferred as a wide type(widening)
let x = "hello"; // Inferred as string
let n = 42; // Inferred as number
let b = true; // Inferred as boolean
// const Inferred as an exact literal type(no widening)
const x2 = "hello"; // Inferred as "hello"(Literal types,No string)
const n2 = 42; // Inferred as 42(Literal types,No number)
const b2 = true; // Inferred as true(Literal types,No boolean)
💡 なぜこのように設計されているのでしょうか?
let 変数には再代入が可能であるため、より広い型を持つと推論されます(string は任意の文字列を格納できます);const 変数は変更されないため、リテラルそのものの型を持つと推論されます。この挙動は 型拡張 と呼ばれます。
(3) ルール 3:初期値が指定されていない場合、any として推定される
変数が初期値や型注釈なしで宣言された場合、TypeScript はそれを any として推論します:
TYPESCRIPT
let something; // Inferred as any(Danger!)
something = 42; // any,Do not check
something = "hello"; // any,Do not check——Completely bypassed the type system
🔥 よくある間違い:
any は事実上型チェックを無効にするため、JavaScript を記述する場合と何ら変わりません。strict モードが有効になっている場合、初期値のない変数を使用するとエラーが発生します(型を明示的に指定するか、初期値を指定する必要があります)。
(4) ルール 4:最適な公開型推論
複数のソースから型を推論する際、TypeScript はその「最適な共通型」を探します:
TYPESCRIPT
let arr = [1, "hello", true];
// Inferred as (number | string | boolean)[] —— Union Type Arrays
let arr2 = [1, 2, 3];
// Inferred as number[] —— All elements are of the same type,Inferred as an array of element types
▶ 例:型推論の実験
TYPESCRIPT
// Experiment1:let vs const Differences in Inference
let dynamicText = "hello";
const fixedText = "hello";
dynamicText = "world"; // ✅ string Can accept another string
// fixedText = "world"; // ❌ const Cannot be reassigned
// Experiment2:Inference of Object Properties
let user = {
name: "Charlie",
age: 20
};
// Inferred as { name: string; age: number }
user.name = "Diana"; // ✅ string Properties can be assigned new values
user.age = 21; // ✅ number Properties can be assigned new values
// Experiment3:const Object——The property can still be modified!
const config = {
host: "localhost",
port: 3000
};
config.host = "127.0.0.1"; // ✅ const Only reassigning the object itself is prohibited.,Non-freezing property
// config = { host: "x", port: 1 }; // ❌ You cannot reassign the entire object
3. 型の互換性の基礎
TypeScriptの型互換性では「構造的サブタイピング」が採用されています。つまり、型名が同じである必要はなく、構造が一致していれば型は互換性があるとみなされます。
(1) サブタイプの互換性
TYPESCRIPT
let num: number = 42;
let big: bigint = 100n;
// num = big; // ❌ bigint No number subtypes of
// big = num; // ❌ number No bigint subtypes of
(2) リテラル型はワイド型のサブタイプである
TYPESCRIPT
let text: string = "hello"; // string Type
const literal: "hello" = "hello"; // Literal types "hello"
text = literal; // ✅ "hello" is a string subtype of,Can be assigned a value
// literal = text; // ❌ string No "hello" subtypes of
📌 例え: リテラル型とワイド型の関係は、「リンゴ」と「果物」の関係に似ています。つまり、リンゴは果物の一種ですが、果物が必ずしもリンゴであるとは限りません。
(3) 「any」は「脱出ポッド」を指す
TYPESCRIPT
let a: any = "hello";
a = 42; // ✅ any Can accept any type
a = true; // ✅ any No tests will be performed
let b: string = a; // ✅ any It can also be assigned to any type.(Danger!)
⚠️ 警告:
any は型安全性を損なうため、以下の状況でのみ使用してください:(1) 迅速なプロトタイピング、(2) レガシー JavaScript コードの移行、または (3) サードパーティ製ライブラリに型定義がない場合の一時的な回避策として。any は通常の開発では使用を避けるべきです。
4. 型アサーションと型推論の組み合わせ
推論結果が期待通りでない場合は、型アサーションを使って上書きすることができます:
TYPESCRIPT
// Scene:DOM Type Inference for Elements
let input = document.getElementById("myInput");
// Inferred as HTMLElement | null——I don't know exactly what element it is.
let inputEl = document.getElementById("myInput") as HTMLInputElement;
// Assert that HTMLInputElement——Tell the compiler"I'm sure this is input Element"
💡 ヒント: 型アサーションは実行時の型変換を行いません。これらはあくまでコンパイル時の型注釈にすぎません。アサーションが正しくない場合、実行時に問題が発生します。これについてはレッスン18で詳しく説明します。
5. 実際の開発における推論戦略
(1) 戦略 1:推測できることは、書き留めない
TYPESCRIPT
// ❌ Redundancy
let name: string = "Charlie";
const MAX: number = 100;
function add(a: number, b: number): number { return a + b; }
// ✅ Concise
let name = "Charlie";
const MAX = 100;
function add(a: number, b: number) { return a + b; } // The return type is inferred as number
(2) 戦略 2:インターフェースと型エイリアスを指定する必要がある
TYPESCRIPT
interface User {
name: string;
age: number;
}
// When an object literal is assigned to a typed variable,Inference+Type checking takes effect simultaneously
let user: User = { name: "Charlie", age: 20 }; // ✅ Structural Matching
let bad: User = { name: "Charlie" }; // ❌ Missing age Properties
(3) 戦略 3:複雑な式に明示的にラベルを付ける
TYPESCRIPT
// Return Values of Complex Functions——It is recommended to explicitly label them.,Avoiding Fallacies in Reasoning
function getUserInfo(id: number): { name: string; age: number } | null {
if (id <= 0) return null;
return { name: "User" + id, age: 20 };
}
▶ 例:実践における推論――構成オブジェクト
TYPESCRIPT
// Use as const assertion to make object properties read-only literal types
const THEME = {
primary: "#4A90D9",
secondary: "#7B51D9",
fontSize: 14
} as const;
// Inference Results:
// { readonly primary: "#4A90D9"; readonly secondary: "#7B51D9"; readonly fontSize: 14 }
// Each property is of an exact literal type + readonly
// THEME.primary = "#000"; // ❌ readonly Properties cannot be modified
// THEME.fontSize = 16; // ❌ readonly Properties cannot be modified
console.log(THEME.primary); // "#4A90D9"
console.log(THEME.fontSize); // 14
出力:
TEXT
#4A90D9
14
💡
as const の目的: TypeScript に対し、値に対して(型を広げることなく)最も正確なリテラル型を推論し、readonly 修飾子を適用するよう指示します。これは、定数設定やアクション型の定義などの場面で一般的に使用されます。
❓ よくある質問
Q 型注釈を手動で指定する必要があるのはどのような場合ですか?
A 以下の3つの状況です:(1) 関数の引数――型推論のための初期値がない場合;(2) 初期値のない変数宣言――そうでない場合、型は
any として推論されます; (3) 期待と一致しない戻り値の型—推論された型が広すぎたり狭すぎたりする場合は、上書きする必要があります。Q
anyとTypeScriptの関係はどのようなものですか?anyを使用するのは良いアイデアでしょうか?A
anyはTypeScriptの「型エスケープハッチ」であり、型チェックを無効にして、事実上JavaScriptモードに戻します。レガシーコードの移行や、一時的な型の問題の回避を行う場合を除き、any を使用すべきではありません。型が不明な場合は、unknown(第5課で詳しく説明しています)を使用する方が、any を使用するよりも安全です。Q
as const とは何ですか?通常の const とどう違うのですか?A 通常の
const は、変数への再代入を防ぐだけですが、型推論では依然として型の拡張が許容されます(例えば、const x = "hi" は "hi" として推論されますが、オブジェクトのプロパティは拡張後の型のままです)。as constは、すべてのレベルがリテラルとreadonlyの組み合わせとして推論されるようにする型アサーションです。オブジェクトや配列に対してas constを使用する場合にのみ、真のディープ読み取り専用挙動を実現できます。📖 まとめ
letを使用して変更可能な変数を宣言し、constを使用して定数を宣言してください。また、varは使用しないでください。- 型推論のルール:初期値が指定されている場合、型は値によって推論される;
constはリテラル型として推論される(型拡張なし);letは拡張型として推論される(型拡張あり);初期値が指定されていない場合、型はanyとして推論される。 any型チェックを無効化。これは可能な限り避けるべきである;as const推論を「正確なリテラル + 読み取り専用」とする- 実際の開発戦略:推測可能な場合は省略可。関数の引数は必ず文書化すること。複雑な戻り値については、文書化することが推奨される。
- 型の互換性は、構造化されたサブタイピングに従います。リテラル型はより広い型と互換性がありますが、その逆は成り立ちません。
📝 練習問題
- 基本演習(難易度 ⭐):
letを使って文字列変数を 1 つ、constを使ってもう 1 つ宣言し、それぞれの値を出力してください。その後、それらに再代入を試みて、TypeScript がどのようにエラーを処理するかを確認してください。 - 上級問題(難易度 ⭐⭐):
COLORSという名前の設定オブジェクトas constを定義し、そこに赤、緑、青の 3 つの 16 進数の色値を格納してください。その後、いずれかの色を変更してみて、readonly制約によって生成されるエラーメッセージを確認してください。 - 課題(難易度:⭐⭐⭐):戻り値の型を明示せずに、TypeScriptに型推論を行わせる関数
createUser(name: string, age: number)を作成してください。その後、関数の外側で型付き変数を使用して戻り値を受け取り、型推論が正しいかどうかを確認してください。



