基本の型
string・number・boolean など TypeScript の基本的な型を学びます
このチャプターで学ぶこと
- プリミティブ型(string, number, boolean)を理解できる
- 配列とオブジェクトの型を定義できる
- any と unknown の違いを理解できる
- null と undefined の扱いを理解できる
基本の型
前のチャプターで TypeScript の環境を整えました。ここからは実際に TypeScript の型を書いていきましょう。PHP や Ruby にも型の概念はありますが、TypeScript はそれよりもはるかに表現力豊かな型システムを持っています。まずは基本中の基本であるプリミティブ型から始めます。
プリミティブ型
TypeScript の最も基本的な型は3つです。JavaScript のプリミティブ型と1対1で対応しています。
// 文字列型
const name: string = "山田太郎";
// 数値型(整数も小数もすべて number)
const age: number = 28;
const price: number = 1980.5;
// 真偽値型
const isActive: boolean = true;
PHP / Ruby との比較
| TypeScript | PHP | Ruby |
|---|---|---|
string | string | String |
number | int / float | Integer / Float |
boolean | bool | TrueClass / FalseClass |
PHP では整数(int)と浮動小数点数(float)が区別されますが、TypeScript ではすべて number 型です。JavaScript の仕様で数値はすべて浮動小数点数として扱われるためです。整数と小数を区別したい場面では、ロジックで対処します。
型が合わないとどうなるか
型注釈と実際の値が一致しないとコンパイルエラーになります。
// すべてコンパイルエラー
const name: string = 123; // エラー: number を string に代入できない
const age: number = "二十八"; // エラー: string を number に代入できない
const isActive: boolean = "true"; // エラー: string を boolean に代入できない
このエラーは実行前に検知されます。PHP で declare(strict_types=1) を有効にした状態に近いですが、TypeScript はさらに厳密です。
配列の型
配列の型定義には2つの書き方があります。どちらを使っても同じ意味です。
// 書き方 1: 型名[] (こちらが主流)
const fruits: string[] = ["りんご", "バナナ", "みかん"];
const scores: number[] = [85, 92, 78, 95];
// 書き方 2: Array<型名>(ジェネリック構文)
const fruits2: Array<string> = ["りんご", "バナナ", "みかん"];
const scores2: Array<number> = [85, 92, 78, 95];
日常的なコードでは string[] のような短い書き方が好まれます。Array<string> はジェネリクスの文脈や、型が複雑になるときに使うことが多いです。どちらを使うかはプロジェクトのコーディング規約に従いましょう。
型付きの配列は、宣言した型と異なる値を追加しようとするとエラーになります。
const fruits: string[] = ["りんご", "バナナ"];
fruits.push("みかん"); // OK
fruits.push(123); // エラー: number を string 型の配列に追加できない
複数の型を混在させたい場合
異なる型の値を混在させたい場合は、ユニオン型(|)を使います。
// string または number の配列
const mixed: (string | number)[] = ["りんご", 100, "バナナ", 200];
ただし、混在型の配列は可読性が下がるため、避けられる場合は避けましょう。
オブジェクトの型
オブジェクトの型はインラインで定義できます。
// オブジェクトの型をインラインで定義
const user: { name: string; age: number; email: string } = {
name: "山田太郎",
age: 28,
email: "yamada@example.com",
};
PHP の連想配列(array)と違い、TypeScript のオブジェクトはどのキーが存在し、各キーの値がどんな型かを厳密に定義できます。
// 定義にないプロパティにアクセスするとエラー
console.log(user.phone); // エラー: 'phone' は存在しない
// 型が合わない値を代入するとエラー
user.age = "二十八"; // エラー: string を number に代入できない
オプショナルプロパティ
必須でないプロパティには ? を付けます。
const user: { name: string; age: number; nickname?: string } = {
name: "山田太郎",
age: 28,
// nickname は省略可能
};
// nickname は string | undefined として扱われる
console.log(user.nickname); // undefined
nickname?: string は「nickname が存在すればそれは string 型で、存在しなくてもよい」という意味です。Laravel の Form Request で 'nickname' => 'nullable|string' と書くのに似ています。
読み取り専用プロパティ
変更されたくないプロパティには readonly を付けます。
const config: { readonly apiUrl: string; readonly timeout: number } = {
apiUrl: "https://api.example.com",
timeout: 5000,
};
config.apiUrl = "https://other.com"; // エラー: readonly プロパティは変更できない
any 型 — 存在理由と危険性
any はすべての型チェックを無効にする「脱出ハッチ」です。
// any を使うとなんでも代入できてしまう
let value: any = "文字列";
value = 123; // エラーなし
value = true; // エラーなし
value = null; // エラーなし
// メソッド呼び出しもチェックされない
value.toFixed(2); // エラーなし(実行時に壊れる可能性あり)
value.nonExistent(); // エラーなし(実行時に壊れる)
なぜ any が存在するのか
any は主に以下の場面で使われます。
- JavaScript からの移行途中 — 既存の JS コードを少しずつ TS に変換するとき、一時的に
anyを使う - 型定義がないサードパーティライブラリ — 型情報が提供されていないライブラリを使うとき
- 非常に動的なデータ — 型を表現するのが極端に難しい場面
なぜ any を避けるべきか
// any を使ったコード — TypeScript を使う意味がなくなる
function processData(data: any) {
return data.name.toUpperCase(); // data に name があるか分からない
}
processData({ name: "太郎" }); // 動く
processData({ title: "記事" }); // 実行時エラー! name が undefined
processData(42); // 実行時エラー! 42.name は undefined
any を使うと TypeScript の型チェックが完全に無効になります。これは PHP で型宣言をすべて取り除くのと同じことです。
tsconfig.json で strict: true を設定すると、noImplicitAny が有効になり、型注釈が省略されたときの暗黙の any がエラーになります。明示的に any と書くことはできますが、コードレビューで指摘されるでしょう。プロジェクトの ESLint ルールで @typescript-eslint/no-explicit-any を有効にしているチームも多いです。
unknown 型 — 安全な代替手段
unknown は any の安全な代替です。どんな値も代入できますが、使うときに型チェックが必要です。
let value: unknown = "文字列";
value = 123; // OK(代入はできる)
value = true; // OK
// しかし、そのまま使おうとするとエラー
console.log(value.toFixed(2)); // エラー: unknown 型のままでは使えない
unknown 型の値を使うには、型を絞り込む(ナローイング)必要があります。
function processValue(value: unknown) {
// typeof で型を確認してから使う
if (typeof value === "string") {
// このブロック内では value は string 型として扱われる
console.log(value.toUpperCase()); // OK
} else if (typeof value === "number") {
// このブロック内では value は number 型として扱われる
console.log(value.toFixed(2)); // OK
} else {
console.log("その他の型です");
}
}
any vs unknown の比較
| 特徴 | any | unknown |
|---|---|---|
| 何でも代入できる | はい | はい |
| そのまま使える | はい(危険) | いいえ(型チェックが必要) |
| 型安全性 | なし | あり |
| 推奨度 | 避ける | 安全に使える |
外部APIからのレスポンスなど、型が不明なデータを受け取る場面では any ではなく unknown を使いましょう。型チェックが強制されるので、データの構造が期待と異なる場合のバグを防げます。Laravel で $request->input() の値をバリデーションしてから使うのと同じ発想です。
null と undefined の扱い
JavaScript には「値がない」ことを表す2つの値があります。TypeScript はそれぞれに専用の型を持っています。
// null: 意図的に「値がない」ことを示す
let result: string | null = null;
result = "データが見つかりました";
// undefined: 値が設定されていない
let name: string | undefined = undefined;
name = "山田太郎";
strictNullChecks の効果
tsconfig.json で strict: true を設定すると strictNullChecks が有効になり、null や undefined の扱いが厳格になります。
// strictNullChecks: true の場合
// string 型に null は代入できない
let name: string = null; // エラー!
// null を許容するにはユニオン型で明示する
let name: string | null = null; // OK
null チェックのパターン
function getUserName(user: { name: string } | null): string {
// null チェックなしでアクセスするとエラー
// return user.name; // エラー: user が null の可能性がある
// パターン 1: if 文でチェック
if (user !== null) {
return user.name; // OK: null が除外されている
}
return "ゲスト";
// パターン 2: オプショナルチェーン + nullish coalescing
// return user?.name ?? "ゲスト";
}
??(nullish coalescing)は左辺が null または undefined のときだけ右辺の値を返します。|| 演算子は空文字列や 0 でも右辺を返してしまうため、?? のほうが安全です。PHP 7.0 の ?? 演算子と同じ挙動です。
オプショナルチェーン(?.)
ネストしたオブジェクトのプロパティに安全にアクセスするには ?. を使います。
interface Company {
name: string;
address?: {
city: string;
zipCode: string;
};
}
function getCityName(company: Company): string {
// address が undefined の場合、city にアクセスせず undefined を返す
return company.address?.city ?? "不明";
}
その他の基本的な型
タプル型
要素の数と各位置の型が固定された配列です。
// [string, number] のタプル
const pair: [string, number] = ["りんご", 150];
// 各要素の型が決まっている
const name: string = pair[0]; // OK
const price: number = pair[1]; // OK
リテラル型
特定の値だけを受け付ける型です。
// "admin" または "user" のどちらかだけ受け付ける
let role: "admin" | "user" = "admin";
role = "user"; // OK
role = "guest"; // エラー: "guest" は許可されていない
// 数値リテラルも使える
let httpStatus: 200 | 404 | 500 = 200;
void 型
関数が何も返さないことを示します。
// 戻り値がない関数
function logMessage(message: string): void {
console.log(message);
// return 文がない、または return; だけ
}
never 型
絶対に値を返さない(正常終了しない)関数に使います。
// 必ず例外を投げる関数
function throwError(message: string): never {
throw new Error(message);
}
// 無限ループ
function infiniteLoop(): never {
while (true) {
// 永遠に終わらない
}
}
void は「値を返さない」、never は「関数が正常に終了しない」という違いがあります。日常的なコードでは void をよく使い、never は例外処理や網羅性チェックで使います。
型の一覧まとめ
ここまで学んだ型を一覧にまとめます。
| 型 | 説明 | 例 |
|---|---|---|
string | 文字列 | "Hello" |
number | 数値(整数・小数) | 42, 3.14 |
boolean | 真偽値 | true, false |
string[] | 文字列の配列 | ["a", "b"] |
number[] | 数値の配列 | [1, 2, 3] |
object | オブジェクト | { name: "太郎" } |
null | null 値 | null |
undefined | 未定義 | undefined |
any | 何でも許容(非推奨) | 型チェック無効 |
unknown | 何でも許容(安全) | 型チェック必須 |
void | 戻り値なし | function f(): void {} |
never | 到達不可能 | throw new Error() |
[A, B] | タプル | ["太郎", 28] |
"a" | "b" | リテラルユニオン | 特定の値のみ |
以下の JavaScript コードに TypeScript の型注釈を追加してみましょう。新しい .ts ファイル(例:src/exercise.ts)を作成して試してください。
問題 1: 変数に型を付ける
// 型を追加してください
const userName = "田中花子";
const userAge = 32;
const isAdmin = false;
const scores = [88, 95, 72, 100];
const tags = ["TypeScript", "React", "Vite"];問題 2: オブジェクトに型を付ける
// 型を追加してください
const product = {
id: 1,
name: "TypeScript入門書",
price: 2980,
inStock: true,
categories: ["プログラミング", "Web開発"],
};問題 3: any を unknown に書き換える
// この関数を any を使わずに安全にする
function formatValue(value: any): string {
if (typeof value === "string") {
return value.toUpperCase();
}
if (typeof value === "number") {
return value.toFixed(2);
}
return String(value);
}解答例:
// 問題 1
const userName: string = "田中花子";
const userAge: number = 32;
const isAdmin: boolean = false;
const scores: number[] = [88, 95, 72, 100];
const tags: string[] = ["TypeScript", "React", "Vite"];
// 問題 2
const product: {
id: number;
name: string;
price: number;
inStock: boolean;
categories: string[];
} = {
id: 1,
name: "TypeScript入門書",
price: 2980,
inStock: true,
categories: ["プログラミング", "Web開発"],
};
// 問題 3(any を unknown に変更するだけ!)
function formatValue(value: unknown): string {
if (typeof value === "string") {
return value.toUpperCase();
}
if (typeof value === "number") {
return value.toFixed(2);
}
return String(value);
}問題 3 では、any を unknown に変えるだけで型安全になります。typeof による型チェックが必須になり、チェックなしでメソッドを呼ぶとエラーになります。
まとめ
このチャプターでは TypeScript の基本的な型を学びました。
- プリミティブ型(
string、number、boolean)は JavaScript の値と1対1で対応する - 配列は
string[]またはArray<string>で型を定義する - オブジェクトはプロパティごとに型を定義でき、
?でオプショナル、readonlyで読み取り専用にできる anyは型チェックを無効にする危険な型で、できるだけ避けるべきunknownはanyの安全な代替で、使う前に型チェックが強制されるnullとundefinedはユニオン型(string | null)で明示し、?.や??で安全に扱う
次のチャプターでは、型注釈を「いつ書くべきか」「いつ省略してよいか」を学ぶ 型注釈と型推論 に進みます。