第1回では、TypeScriptの全体像と基本的な型を学びました。
TypeScriptを学ぶ上で欠かせない「型」の理解を一歩進めたい方へ──。
TypeScript入門 第2回:「型の基礎を深く理解する」は、前回の基本型の紹介からステップアップし、プリミティブ型や配列、オブジェクト型などの詳細な仕組みと、「実践でどう使うのか」を具体例を交えて丁寧に解説します。
実務での活用にも役立つヒント満載で、TypeScriptの型安全性や表現力をしっかり身につけられる内容です。
Contents
プリミティブ型の詳細
string型
文字列を扱う最も基本的な型です。
let message: string = "Hello, TypeScript!";
let name: string = '太郎'; // シングルクォートもOK
// テンプレートリテラル
let greeting: string = `こんにちは、${name}さん`;
// 複数行の文字列
let multiLine: string = `
これは
複数行の
文字列です
`;
リテラル型:特定の文字列だけを許可
// "success", "error", "warning"のいずれかのみ許可
let status: "success" | "error" | "warning";
status = "success"; // OK
status = "error"; // OK
// status = "info"; // エラー!指定された値以外は不可
// 実用例:APIのレスポンスステータス
type ResponseStatus = "success" | "error" | "pending";
function handleResponse(status: ResponseStatus): void {
if (status === "success") {
console.log("成功しました");
} else if (status === "error") {
console.log("エラーが発生しました");
}
}
number型
数値を扱う型です。
整数、小数、負の数、すべて同じnumber型です。
let integer: number = 42;
let float: number = 3.14;
let negative: number = -10;
let hex: number = 0xff; // 16進数
let binary: number = 0b1010; // 2進数
let octal: number = 0o744; // 8進数
// 特殊な数値
let infinity: number = Infinity;
let notANumber: number = NaN;
Infinity とは、
「正の無限大」を表す特別な数値です。
NaN(Not a Number) とは、
「数値として計算できない結果」 を表す 特別な値です。
| 値 | 意味 | 型 |
|---|---|---|
| Infinity | 無限大 | number |
| -Infinity | 負の無限大 | number |
| NaN | 数値にならない | number |
数値リテラル型
// 特定の数値のみ許可
let dice: 1 | 2 | 3 | 4 | 5 | 6;
dice = 3; // OK
// dice = 7; // エラー!
// 実用例:HTTP ステータスコード
type HttpStatus = 200 | 201 | 400 | 401 | 404 | 500;
function handleHttpStatus(status: HttpStatus): string {
switch (status) {
case 200:
return "OK";
case 404:
return "Not Found";
case 500:
return "Internal Server Error";
default:
return "Unknown Status";
}
}
boolean型
true/falseの真偽値を扱います。
let isActive: boolean = true;
let hasPermission: boolean = false;
// 条件式の結果も boolean
let isAdult: boolean = age >= 18;
let canVote: boolean = isAdult && hasPermission;
booleanリテラル型
// trueのみ許可
let alwaysTrue: true = true;
// alwaysTrue = false; // エラー!
// 実用例:機能フラグ
type FeatureFlag = {
darkMode: boolean;
betaFeatures: true; // ベータ機能は常にtrue
};
null と undefined
JavaScriptには「値がない」を表す2つの型があります。
let empty: null = null;
let notAssigned: undefined = undefined;
// 実務では Union型と組み合わせることが多い
let nullableString: string | null = null;
nullableString = "値を設定";
nullableString = null; // OK
let optionalNumber: number | undefined = undefined;
optionalNumber = 42;
optionalNumber = undefined; // OK
strictNullChecksオプション
tsconfig.jsonでstrictNullChecks: trueにすると、nullとundefinedのチェックが厳密になります(推奨)。
// strictNullChecks: true の場合
let name: string = "太郎";
// name = null; // エラー!
// nullを許可したい場合は明示的に
let nullableName: string | null = "太郎";
nullableName = null; // OK
symbol と bigint
あまり使わないかもしれませんが、覚えておきましょう。
// symbol:一意な識別子
let sym1: symbol = Symbol("key");
let sym2: symbol = Symbol("key");
console.log(sym1 === sym2); // false(それぞれユニーク)
// bigint:巨大な整数(ES2020以降)
let big: bigint = 100n;
let huge: bigint = BigInt("9007199254740991");
配列の詳細
基本的な配列の型定義
// 方法1:型[]
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["太郎", "花子", "次郎"];
// 方法2:Array<型>(ジェネリック記法)
let colors: Array<string> = ["red", "blue", "green"];
let scores: Array<number> = [80, 90, 75];
どちらも同じ意味ですが、number[]の方がシンプルで読みやすいため、こちらが推奨されます。
多次元配列
// 2次元配列
let matrix: number[][] = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 3次元配列
let cube: number[][][] = [
[[1, 2], [3, 4]],
[[5, 6], [7, 8]]
];
// 実用例:座標の配列
let coordinates: [number, number][] = [
[10, 20],
[30, 40],
[50, 60]
];
タプル(Tuple):固定長で型が異なる配列
タプル(Tuple) とは、
👉 「要素数と順番ごとに型が決まっている配列」 です。
配列の各要素に異なる型を指定できます。
// [文字列, 数値]のペア
let user: [string, number] = ["太郎", 25];
// [ID, 名前, アクティブ状態]
let record: [number, string, boolean] = [1, "商品A", true];
// 実用例:座標
let point: [number, number] = [10, 20];
let point3D: [number, number, number] = [10, 20, 30];
// 実用例:関数の戻り値(エラーとデータのペア)
function fetchData(): [Error | null, string | null] {
try {
return [null, "データ取得成功"];
} catch (error) {
return [new Error("エラー"), null];
}
}
const [error, data] = fetchData();
if (error) {
console.error(error);
} else {
console.log(data);
}
タプルのオプショナル要素とrest要素
オプショナル要素(optional element) とは、
👉 「あってもなくてもいい要素・プロパティ」 を表す仕組みです。
rest要素(Rest Element / Rest Parameters) は、
👉 「残りの値をひとまとめにして受け取る仕組み」 です。…(三点リーダ)を使います。
// オプショナル要素(?を付ける)
let optional: [string, number?] = ["太郎"];
optional = ["花子", 30]; // 両方OK
// rest要素(可変長)
let scores: [string, ...number[]] = ["太郎", 80, 90, 75];
readonly配列
変更不可能な配列を定義できます。
// 読み取り専用配列
let readonlyNumbers: readonly number[] = [1, 2, 3];
// readonlyNumbers.push(4); // エラー!変更不可
// readonlyNumbers[0] = 10; // エラー!要素の変更も不可
// ReadonlyArray<T>も同じ意味
let readonlyNames: ReadonlyArray<string> = ["太郎", "花子"];
配列の型推論
型推論(type inference) とは、
👉 プログラマが型を書かなくても、言語やコンパイラが自動で型を判断してくれる仕組みです。
// TypeScriptは初期値から型を推論する
let numbers = [1, 2, 3]; // number[]と推論
let mixed = [1, "two", 3]; // (number | string)[]と推論
// 空配列の場合は注意
let empty = []; // any[]と推論されてしまう(避けるべき)
let emptyNumbers: number[] = []; // 型を明示するべき
オブジェクトの詳細
基本的なオブジェクト型
// インライン型定義
let user: { name: string; age: number } = {
name: "太郎",
age: 25
};
// オブジェクトのネスト
let person: {
name: string;
age: number;
address: {
zip: string;
city: string;
};
} = {
name: "花子",
age: 30,
address: {
zip: "100-0001",
city: "東京都"
}
};
オプショナルプロパティ
プロパティ名の後ろに?を付けると、省略可能になります。
type User = {
name: string;
age: number;
email?: string; // オプショナル
phone?: string; // オプショナル
};
// emailとphoneは省略可能
let user1: User = {
name: "太郎",
age: 25
};
let user2: User = {
name: "花子",
age: 30,
email: "hanako@example.com"
};
readonlyプロパティ
変更不可能なプロパティを定義できます。
type Config = {
readonly apiKey: string;
readonly endpoint: string;
timeout: number; // 通常のプロパティ
};
let config: Config = {
apiKey: "abc123",
endpoint: "https://api.example.com",
timeout: 3000
};
config.timeout = 5000; // OK
// config.apiKey = "xyz"; // エラー!readonlyは変更不可
インデックスシグネチャ
プロパティ名が動的な場合に使います。
// 任意の文字列キーで数値を持つオブジェクト
type Dictionary = {
[key: string]: number;
};
let scores: Dictionary = {
"math": 90,
"english": 85,
"science": 92
};
scores["history"] = 88; // 動的に追加可能
// 実用例:ユーザー情報の辞書
type UserDatabase = {
[userId: string]: {
name: string;
email: string;
};
};
let users: UserDatabase = {
"user001": { name: "太郎", email: "taro@example.com" },
"user002": { name: "花子", email: "hanako@example.com" }
};
インデックスシグネチャと通常のプロパティの組み合わせ
type MixedType = {
name: string; // 必須プロパティ
age: number; // 必須プロパティ
[key: string]: any; // その他は任意
};
let person: MixedType = {
name: "太郎",
age: 25,
hobby: "reading", // 追加のプロパティOK
country: "Japan" // 追加のプロパティOK
};
Type Alias vs Interface
オブジェクトの型定義には2つの方法があります。
Type Alias
type User = {
name: string;
age: number;
};
type Admin = User & {
role: "admin";
permissions: string[];
};
Interface
interface User {
name: string;
age: number;
}
interface Admin extends User {
role: "admin";
permissions: string[];
}
主な違い
// 1. Type Aliasは様々な型に使える
type ID = string | number; // Union型
type Point = [number, number]; // タプル
// 2. Interfaceは宣言のマージが可能
interface Window {
title: string;
}
interface Window {
width: number;
}
// 自動的にマージされる
const w: Window = { title: "App", width: 800 };
// 3. Type Aliasは型の計算が可能
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
どちらを使うべき?
- オブジェクトの形を定義するなら、基本的にInterfaceを使う(拡張しやすい)
- Union型やタプルなど、複雑な型定義にはType Aliasを使う
- チーム内で統一されていればどちらでもOK
ネストしたオブジェクト
実務では複雑なオブジェクト構造を扱います。
type Company = {
name: string;
employees: {
id: number;
name: string;
department: {
id: number;
name: string;
manager: {
id: number;
name: string;
};
};
}[];
};
// より読みやすく分割する
type Manager = {
id: number;
name: string;
};
type Department = {
id: number;
name: string;
manager: Manager;
};
type Employee = {
id: number;
name: string;
department: Department;
};
type Company2 = {
name: string;
employees: Employee[];
};
実践例:ECサイトの商品管理
学んだ知識を使って、実践的な型定義を作ってみましょう。
// 商品カテゴリ(リテラル型)
type Category = "electronics" | "clothing" | "food" | "books";
// 在庫ステータス
type StockStatus = "in_stock" | "low_stock" | "out_of_stock";
// レビュー
type Review = {
id: number;
userId: string;
rating: 1 | 2 | 3 | 4 | 5; // 1〜5の評価
comment: string;
createdAt: Date;
};
// 商品
type Product = {
id: number;
name: string;
description: string;
price: number;
category: Category;
stockStatus: StockStatus;
images: string[]; // 画像URLの配列
tags: readonly string[]; // タグは変更不可
specifications?: { // 仕様(オプショナル)
[key: string]: string | number;
};
reviews: Review[];
};
// 使用例
const laptop: Product = {
id: 1,
name: "高性能ノートPC",
description: "最新のプロセッサを搭載",
price: 150000,
category: "electronics",
stockStatus: "in_stock",
images: [
"https://example.com/laptop1.jpg",
"https://example.com/laptop2.jpg"
],
tags: ["人気", "新製品"],
specifications: {
cpu: "Intel Core i7",
ram: 16,
storage: 512
},
reviews: [
{
id: 1,
userId: "user001",
rating: 5,
comment: "とても満足しています",
createdAt: new Date("2024-01-15")
}
]
};
// 商品を検索する関数
function findProductsByCategory(
products: Product[],
category: Category
): Product[] {
return products.filter(p => p.category === category);
}
// 在庫が少ない商品を取得
function getLowStockProducts(products: Product[]): Product[] {
return products.filter(p => p.stockStatus === "low_stock");
}
// 平均評価を計算
function calculateAverageRating(product: Product): number {
if (product.reviews.length === 0) return 0;
const sum = product.reviews.reduce((acc, review) => acc + review.rating, 0);
return sum / product.reviews.length;
}
型の互換性
TypeScriptは構造的型付け(Structural Typing)を採用しています。
type Point2D = {
x: number;
y: number;
};
type Point3D = {
x: number;
y: number;
z: number;
};
let point2D: Point2D = { x: 10, y: 20 };
let point3D: Point3D = { x: 10, y: 20, z: 30 };
// Point3DはPoint2Dと互換性がある(余分なプロパティがあっても可)
point2D = point3D; // OK
// 逆は不可(zが足りない)
// point3D = point2D; // エラー!
よくあるパターンとアンチパターン
✅ 良い例
// 明確な型定義
type User = {
id: number;
name: string;
email: string;
};
// 適切な Union型の使用
type Result = { success: true; data: string } | { success: false; error: string };
// readonlyで不変性を保証
type Config = {
readonly apiKey: string;
};
❌ 避けるべき例
// any型の使用(型安全性が失われる)
let data: any = "文字列";
data.toString(); // エラーチェックされない
// 過度に複雑なネスト
type BadType = {
a: {
b: {
c: {
d: {
e: string; // 深すぎる
};
};
};
};
};
// 型を明示しない空配列
let items = []; // any[]になってしまう
まとめ
今回は、TypeScriptの型システムの基礎を深く学びました。
学んだこと
- プリミティブ型の詳細とリテラル型
- 配列、タプル、readonly配列
- オブジェクトの型定義とオプショナルプロパティ
- Type AliasとInterfaceの使い分け
- インデックスシグネチャ
- 実践的な型定義の例
次回予告 第3回では、関数の型定義とジェネリクスについて学びます。より柔軟で再利用可能なコードを書けるようになります。
TypeScript入門シリーズ
第1回: 環境構築から全体像まで
第2回: 型の基礎を深く理解する
第3回: 関数と型、ジェネリクス入門
第4回: インターフェースとクラス
第5回: 実践編 – 実際のプロジェクトでの活用


























