Chapter 01

基本の型

string・number・boolean など TypeScript の基本的な型を学びます

25 min

このチャプターで学ぶこと

  • プリミティブ型(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 との比較

TypeScriptPHPRuby
stringstringString
numberint / floatInteger / Float
booleanboolTrueClass / 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 は主に以下の場面で使われます。

  1. JavaScript からの移行途中 — 既存の JS コードを少しずつ TS に変換するとき、一時的に any を使う
  2. 型定義がないサードパーティライブラリ — 型情報が提供されていないライブラリを使うとき
  3. 非常に動的なデータ — 型を表現するのが極端に難しい場面

なぜ 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.jsonstrict: true を設定すると、noImplicitAny が有効になり、型注釈が省略されたときの暗黙の any がエラーになります。明示的に any と書くことはできますが、コードレビューで指摘されるでしょう。プロジェクトの ESLint ルールで @typescript-eslint/no-explicit-any を有効にしているチームも多いです。

unknown 型 — 安全な代替手段

unknownany の安全な代替です。どんな値も代入できますが、使うときに型チェックが必要です。

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 の比較

特徴anyunknown
何でも代入できるはいはい
そのまま使えるはい(危険)いいえ(型チェックが必要)
型安全性なしあり
推奨度避ける安全に使える
💡 ヒント

外部APIからのレスポンスなど、型が不明なデータを受け取る場面では any ではなく unknown を使いましょう。型チェックが強制されるので、データの構造が期待と異なる場合のバグを防げます。Laravel で $request->input() の値をバリデーションしてから使うのと同じ発想です。

nullundefined の扱い

JavaScript には「値がない」ことを表す2つの値があります。TypeScript はそれぞれに専用の型を持っています。

// null: 意図的に「値がない」ことを示す
let result: string | null = null;
result = "データが見つかりました";

// undefined: 値が設定されていない
let name: string | undefined = undefined;
name = "山田太郎";

strictNullChecks の効果

tsconfig.jsonstrict: true を設定すると strictNullChecks が有効になり、nullundefined の扱いが厳格になります。

// 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: "太郎" }
nullnull 値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: anyunknown に書き換える

// この関数を 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 では、anyunknown に変えるだけで型安全になります。typeof による型チェックが必須になり、チェックなしでメソッドを呼ぶとエラーになります。

まとめ

このチャプターでは TypeScript の基本的な型を学びました。

次のチャプターでは、型注釈を「いつ書くべきか」「いつ省略してよいか」を学ぶ 型注釈と型推論 に進みます。