TypeScript開発者のための実践的ベストプラクティス2025
実際のプロジェクトで使えるTypeScriptのベストプラクティスを、型安全性、パフォーマンス、保守性の観点から解説します。
約5分で読了
Tech Blog
中級
TypeScript Node.js ESLint

目次
TypeScript開発者のための実践的ベストプラクティス2025
TypeScriptは現在、JavaScript開発において欠かせない技術となっています。しかし、TypeScriptの真価を発揮するには、適切な使い方を理解することが重要です。
この記事では、実際のプロジェクトで使えるTypeScriptのベストプラクティスを、型安全性、パフォーマンス、保守性の観点から解説します。
基本的な型設計
1. Union Types vs Enums
良い例(Union Types):
type Status = 'pending' | 'approved' | 'rejected';
interface User {
id: string;
name: string;
status: Status;
}
避けるべき例(Enums):
// 実行時にコードが生成される
enum Status {
Pending = 'pending',
Approved = 'approved',
Rejected = 'rejected'
}
Union Typesは型レベルでのチェックのみで、実行時にコードを生成しないため、バンドルサイズが小さくなります。
2. 厳密な型定義
// ❌ 緩い型定義
interface ApiResponse {
data: any;
error?: string;
}
// ✅ 厳密な型定義
interface ApiResponse<T> {
data: T | null;
error: string | null;
status: 'success' | 'error';
}
interface User {
readonly id: string;
name: string;
email: string;
createdAt: Date;
}
type ApiUserResponse = ApiResponse<User[]>;
高度な型テクニック
1. Mapped Types
// 既存の型から新しい型を生成
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 実用例
interface User {
id: string;
name: string;
email: string;
}
type UserUpdate = Partial<User>; // すべてオプショナル
type UserCreate = Omit<User, 'id'>; // idを除外
2. Conditional Types
type NonNullable<T> = T extends null | undefined ? never : T;
type ApiResult<T> = T extends string
? { message: T }
: T extends number
? { code: T }
: { data: T };
// 使用例
type StringResult = ApiResult<string>; // { message: string }
type NumberResult = ApiResult<number>; // { code: number }
type UserResult = ApiResult<User>; // { data: User }
エラーハンドリング
1. Result Pattern
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
async function fetchUser(id: string): Promise<Result<User>> {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
return { success: false, error: new Error('User not found') };
}
const user = await response.json();
return { success: true, data: user };
} catch (error) {
return { success: false, error: error as Error };
}
}
// 使用例
const userResult = await fetchUser('123');
if (userResult.success) {
console.log(userResult.data.name); // 型安全
} else {
console.error(userResult.error.message);
}
2. Custom Error Types
abstract class AppError extends Error {
abstract readonly code: string;
abstract readonly statusCode: number;
}
class ValidationError extends AppError {
readonly code = 'VALIDATION_ERROR';
readonly statusCode = 400;
constructor(
message: string,
public readonly field: string
) {
super(message);
}
}
class NotFoundError extends AppError {
readonly code = 'NOT_FOUND';
readonly statusCode = 404;
}
パフォーマンス最適化
1. 型のキャッシュ
// ❌ 毎回計算される
type ExpensiveType<T> = T extends string ? ComplexOperation<T> : never;
// ✅ キャッシュされる
type CachedExpensiveType<T> = T extends string
? T extends infer U
? U extends string
? ComplexOperation<U>
: never
: never
: never;
2. 遅延型評価
// 必要時のみ型を計算
type LazyType<T> = T extends any ? ComputeType<T> : never;
開発体験の向上
1. TypeScript設定
// tsconfig.json
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true
}
}
2. ESLint設定
// eslint.config.js
export default [
{
rules: {
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/prefer-nullish-coalescing': 'error',
'@typescript-eslint/prefer-optional-chain': 'error',
'@typescript-eslint/no-unused-vars': 'error'
}
}
];
実用的なユーティリティ型
// Deep Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// Pick by Value Type
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
// Example
interface User {
id: string;
name: string;
age: number;
isActive: boolean;
}
type StringFields = PickByType<User, string>; // { id: string; name: string }
type NumberFields = PickByType<User, number>; // { age: number }
まとめ
TypeScriptの効果的な活用には:
- 型安全性の追求:
any
の使用を避け、厳密な型定義 - パフォーマンス意識:型計算コストの考慮
- 開発体験:適切な設定とツールの活用
- 保守性:理解しやすい型設計
これらを意識することで、より良いTypeScriptコードを書けるようになります。
TypeScriptは単なる型付きJavaScriptではなく、設計を支援する強力な言語です。型システムを理解し、適切に活用することで、バグの少ない保守しやすいコードを書けるようになります。