※本記事には広告(A8・楽天アフィリエイト)リンクが含まれる場合があります。
「JavaScriptで書いてるけど、バグが多くて困っている」「TypeScriptを試したいけど何から始めればいいかわからない」——個人開発者からよく聞く悩みです。TypeScriptを使いこなせるようになると、コードの品質が劇的に上がり、リファクタリングや機能追加が格段に楽になります。
この記事では、個人開発の現場で本当に使えるTypeScriptの知識を厳選して解説します。基本的な型定義から、実行時バリデーションに欠かせないZod、そして見落としがちなtsconfig設定まで、コード例たっぷりでお伝えします。
なぜTypeScriptを使うべきか
JavaScriptの「見えないバグ」を防ぐ
JavaScriptは柔軟な反面、実行してみて初めて気づくバグが多い言語です。たとえばAPIレスポンスの型が変わっていても、JavaScriptではエラーが出ずに無言で壊れることがあります。TypeScriptはコンパイル時に型エラーを検出するため、こうした問題を事前に潰せます。
// JavaScript — 実行時まで気づかない
function greet(user) {
return `こんにちは、${user.name}さん`;
}
greet(undefined); // 実行時エラー: Cannot read properties of undefined
// TypeScript — コンパイル時にエラー検出
function greet(user: { name: string }): string {
return `こんにちは、${user.name}さん`;
}
greet(undefined); // エラー: Argument of type 'undefined' is not assignable...
IDEのサポートが劇的に改善される
TypeScriptを使うと、VS CodeやCursorなどのエディタが型情報に基づいた補完・リファクタリングを提供してくれます。オブジェクトのプロパティを`. `で入力するだけで候補が表示され、ミスタイプも即座に検出されます。個人開発での生産性が大きく変わります。
基本的な型の書き方
プリミティブ型と配列
TypeScriptの型注釈(アノテーション)は変数名の後ろに : 型名 の形で書きます。
// プリミティブ型
const title: string = "TypeScript入門";
const count: number = 42;
const isPublished: boolean = true;
// 配列
const tags: string[] = ["typescript", "web", "個人開発"];
const scores: number[] = [90, 85, 92];
// オブジェクト
const user: { id: number; name: string; email: string } = {
id: 1,
name: "田中太郎",
email: "tanaka@example.com"
};
ただし、TypeScriptには型推論(Type Inference)があるため、初期値から型が明らかな場合は注釈を省略できます。過剰な型注釈はコードを冗長にするので注意しましょう。
// 型推論が働くので注釈不要
const title = "TypeScript入門"; // string型として推論
const count = 42; // number型として推論
interfaceとtype aliasの使い分け
オブジェクトの型定義には interface と type の2種類があります。どちらを使うべきか迷う方も多いですが、2026年時点での実践的な使い分けは以下の通りです。
// interface — オブジェクト型の定義に使う(宣言マージができる)
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
// interface の拡張(継承)
interface AdminUser extends User {
role: "admin" | "superadmin";
permissions: string[];
}
// type — ユニオン型や複雑な型合成に使う
type Status = "active" | "inactive" | "suspended";
type ID = string | number;
// type は交差型(&)でオブジェクトを合成できる
type AuditableUser = User & {
createdBy: string;
updatedAt: Date;
};
迷ったらinterface、ユニオン型が必要なときはtypeと覚えると実践では困りません。
ユニオン型・リテラル型・型の絞り込み
ユニオン型で「どれかひとつ」を表現する
ユニオン型(A | B)は「AかBのどちらか」という型を作ります。個人開発でAPIのレスポンスやステータス管理に非常によく使います。
type ApiResponse =
| { success: true; data: T }
| { success: false; error: string };
async function fetchUser(id: string): Promise> {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) return { success: false, error: "取得失敗" };
const data = await res.json();
return { success: true, data };
} catch (e) {
return { success: false, error: String(e) };
}
}
// 呼び出し側でユニオン型を絞り込む
const result = await fetchUser("123");
if (result.success) {
console.log(result.data.name); // data は User 型として確定
} else {
console.error(result.error); // error は string 型として確定
}
リテラル型で特定の値だけを許可する
// 文字列リテラル型
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
type Theme = "light" | "dark" | "system";
// 数値リテラル型
type HttpStatusCode = 200 | 201 | 400 | 401 | 403 | 404 | 500;
function makeRequest(url: string, method: HttpMethod) {
return fetch(url, { method });
}
makeRequest("/api/users", "GET"); // OK
makeRequest("/api/users", "PURGE"); // コンパイルエラー!
Zodによるランタイムバリデーション
TypeScriptだけでは不十分な理由
TypeScriptの型チェックはコンパイル時のみ機能します。外部APIやフォーム入力など、実行時に入ってくるデータは型が保証されません。ここでZodが活躍します。
Zodはスキーマ定義と型推論を同時に行えるバリデーションライブラリで、現在の個人開発TypeScriptスタックではほぼ必須と言えます。当ブログのHono + Cloudflare Workers入門でも、APIのバリデーションにZodを活用しています。
import { z } from "zod";
// スキーマを定義
const UserSchema = z.object({
id: z.number().positive(),
name: z.string().min(1).max(50),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
role: z.enum(["user", "admin"]).default("user"),
});
// スキーマからTypeScriptの型を自動生成
type User = z.infer;
// 実行時バリデーション
function processUser(rawData: unknown) {
const result = UserSchema.safeParse(rawData);
if (!result.success) {
console.error("バリデーションエラー:", result.error.flatten());
return null;
}
return result.data; // User型として安全に使える
}
Zodでネストしたスキーマを定義する
const ArticleSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(100),
tags: z.array(z.string()).max(10),
author: z.object({
id: z.number(),
name: z.string(),
}),
publishedAt: z.string().datetime().nullable(),
metadata: z.record(z.string(), z.unknown()).optional(),
});
typ
e Article = z.infer;
ZodはCloudflare Workersのエッジランタイムでも動作するため、Cloudflare WorkersでマイクロSaaSを作る場合も安心して使えます。
tsconfig.jsonの実践的な設定
個人開発プロジェクトに適した設定
TypeScriptの動作はすべて tsconfig.json で制御します。デフォルト設定は甘いので、個人開発では以下の設定を基準にしましょう。
{
"compilerOptions": {
// 基本設定
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
// 出力設定
"outDir": "./dist",
"rootDir": "./src",
"declaration": true,
"sourceMap": true,
// 型チェック(厳格に)
"strict": true, // これ1つで以下の設定を全てONにする
"noImplicitAny": true, // strictに含まれるが明示的に
"noUnusedLocals": true, // 使っていない変数をエラーに
"noUnusedParameters": true,
"noImplicitReturns": true, // 全ての分岐でreturnを強制
// モジュール設定
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true,
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
特に重要なのが "strict": true です。これを有効にするだけで strictNullChecks・noImplicitAny・strictFunctionTypes など複数の厳格チェックがまとめてONになります。プロジェクト開始時から有効にするのが鉄則です(後から有効にするとエラー修正が大変になります)。
パスエイリアスを設定してインポートを簡潔に
{
"compilerOptions": {
// ...上記の設定に追記
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@components/*": ["./src/components/*"],
"@utils/*": ["./src/utils/*"],
"@types/*": ["./src/types/*"]
}
}
}
// 使用例(相対パスが不要になる)
// Before: import { formatDate } from "../../utils/date";
// After: import { formatDate } from "@utils/date";
実践的なTypeScriptパターン集
型ガード(Type Guard)で型を絞り込む
// カスタム型ガード
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"name" in value &&
"email" in value
);
}
// 使用例
const data: unknown = await fetch("/api/me").then(r => r.json());
if (isUser(data)) {
console.log(data.name); // User型として使える
}
ユーティリティ型を活用する
TypeScriptには既存の型から新しい型を作るユーティリティ型が組み込まれています。個人開発でよく使うものをまとめます。
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
// Partial — 全プロパティをオプションにする(PATCH更新時など)
type UserUpdateInput = Partial;
// Omit — 特定のプロパティを除外する
type PublicUser = Omit; // パスワードを除いた型
// Pick — 特定のプロパティだけを選ぶ
type UserSummary = Pick;
// Required — 全プロパティを必須にする
type RequiredUser = Required;
// Record — キーと値の型を指定したオブジェクト
type UserMap = Record; // { [key: string]: User }
// ReadOnly — 全プロパティを読み取り専用にする
type ReadonlyUser = Readonly;
非同期処理と型の扱い
// Promise の型を明示する
async function getArticle(id: string): Promise {
const res = await fetch(`/api/articles/${id}`);
if (!res.ok) return null;
return res.json() as Promise;
}
// エラーハンドリング付きのラッパー型
type Result =
| { ok: true; value: T }
| { ok: false; error: E };
async function safeGetArticle(id: string): Promise> {
try {
const article = await getArticle(id);
if (!article) return { ok: false, error: new Error("記事が見つかりません") };
return { ok: true, value: article };
} catch (e) {
return { ok: false, error: e instanceof Error ? e : new Error(String(e)) };
}
}
個人開発でのTypeScript活用事例
TypeScriptは実際の個人開発プロダクトで大きな威力を発揮します。たとえば当サイトが開発・運営している以下のツールも、すべてTypeScriptで構築されています。
- QuickSummary — AI要約Chrome拡張。Zodでバックエンドのレスポンス型を保証し、型安全なChrome Extension APIの呼び出しを実現
- PagePulse — Web監視ツール。Cloudflare WorkersのAPIとフロントエンドが同じ型定義を共有することで、APIの変更を安全に追跡
- StatusCraft — ステータスページツール。ユニオン型でインシデントのステータス管理を型安全に実装
TypeScriptの型定義をフロントエンドとバックエンドで共有するアーキテクチャについては、Cloudflare D1完全ガイドやSupabase個人開発ガイドでも詳しく解説しています。
TypeScriptの学習ロードマップ
ステップ1:基本(1〜2週間)
- プリミティブ型・配列・オブジェクト型の書き方
- 関数の型注釈(引数・戻り値)
- interfaceとtype aliasの基本
- 型推論を理解して過剰な注釈を避ける
ステップ2:中級(2〜4週間)
- ユニオン型・リテラル型・型の絞り込み
- ジェネリクス(
<T>)の基本的な使い方 - ユーティリティ型(Partial・Omit・Pick など)
- tsconfig.jsonの設定と
strictモード
ステップ3:実践(継続的に)
- Zodとの組み合わせでランタイムバリデーション
- 型ガードとdiscriminated union pattern
- 実際のプロジェクトに適用してエラーを直す経験を積む
- Drizzle ORM・Honoなど型安全なライブラリと組み合わせる
TypeScriptは「完璧に理解してから使う」言語ではありません。実際のプロジェクトで使いながら少しずつ理解を深めるのが最も効率的です。最初は any を使ってしまっても構いません。少しずつ型を付けていくリファクタリングの繰り返しが力になります。
TypeScriptで作るWebツールの次のステップ
TypeScriptをマスターしたら、型安全なWebアプリを本番環境にデプロイしてみましょう。Cloudflare WorkersやPagesなら無料プランで始められます。