tech3分で読める

TypeScriptで型安全なコードを書くためのベストプラクティス

TypeScriptを使った開発で、より型安全で保守性の高いコードを書くためのベストプラクティスをまとめました。

#typescript#best-practices#type-safety#development#コーディング規約

TypeScriptで型安全なコードを書くためのベストプラクティス

TypeScriptを使った開発では、適切な型定義により、バグの早期発見と保守性の向上が可能です。今回は、実践的なベストプラクティスをまとめました。

1. 厳密な型チェックを有効にする

tsconfig.jsonの設定

{ "compilerOptions": { "strict": true, "noImplicitAny": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": true, "exactOptionalPropertyTypes": true } }

2. 適切な型定義

ユニオン型の活用

// ❌ 良くない例 type Status = string; // ✅ 良い例 type Status = 'pending' | 'approved' | 'rejected'; const updateStatus = (status: Status) => { // statusは3つの値のみを取ることが保証される };

インターフェースの適切な定義

// ユーザー情報の基本型 interface User { readonly id: string; name: string; email: string; createdAt: Date; updatedAt: Date; } // API レスポンス用の型 interface ApiResponse<T> { data: T; status: 'success' | 'error'; message?: string; } // 使用例 type UserResponse = ApiResponse<User>;

3. Genericsの効果的な使い方

型安全なAPI関数

async function fetchData<T>( url: string, validator: (data: unknown) => data is T ): Promise<T> { const response = await fetch(url); const data = await response.json(); if (!validator(data)) { throw new Error('Invalid data format'); } return data; } // 型ガード関数 const isUser = (data: unknown): data is User => { return ( typeof data === 'object' && data !== null && 'id' in data && 'name' in data && 'email' in data ); }; // 使用例 const user = await fetchData('/api/user/123', isUser); // userの型はUserとして推論される

4. Utility Typesの活用

よく使うUtility Types

interface Product { id: string; name: string; price: number; description: string; category: string; } // 部分的な更新用 type ProductUpdate = Partial<Product>; // ID以外のフィールド type CreateProduct = Omit<Product, 'id'>; // 特定のフィールドのみ type ProductSummary = Pick<Product, 'id' | 'name' | 'price'>; // 読み取り専用 type ReadonlyProduct = Readonly<Product>;

5. 型ガードとアサーション

カスタム型ガード

// 型ガード関数 const isString = (value: unknown): value is string => { return typeof value === 'string'; }; const isNumber = (value: unknown): value is number => { return typeof value === 'number' && !isNaN(value); }; // 使用例 const processValue = (value: unknown) => { if (isString(value)) { // この時点でvalueはstring型 return value.toUpperCase(); } if (isNumber(value)) { // この時点でvalueはnumber型 return value.toFixed(2); } throw new Error('Unsupported value type'); };

6. エラーハンドリングの型安全性

Result型パターン

type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E }; const safeParseJson = <T>( json: string, validator: (data: unknown) => data is T ): Result<T> => { try { const data = JSON.parse(json); if (validator(data)) { return { success: true, data }; } return { success: false, error: new Error('Validation failed') }; } catch (error) { return { success: false, error: error instanceof Error ? error : new Error('Parse failed') }; } }; // 使用例 const result = safeParseJson(jsonString, isUser); if (result.success) { // result.dataはUser型 console.log(result.data.name); } else { // result.errorはError型 console.error(result.error.message); }

7. 型推論を活用する

constアサーション

// ❌ string[]として推論される const colors = ['red', 'green', 'blue']; // ✅ readonly ['red', 'green', 'blue']として推論される const colors = ['red', 'green', 'blue'] as const; type Color = typeof colors[number]; // 'red' | 'green' | 'blue'

8. モジュール境界での型安全性

型定義ファイルの分離

// types/api.ts export interface ApiUser { id: string; username: string; email: string; } export interface ApiError { code: string; message: string; } // lib/api.ts import type { ApiUser, ApiError } from '../types/api'; export const getUser = async (id: string): Promise<ApiUser> => { // 実装 };

まとめ

TypeScriptの型システムを効果的に活用することで:

実行時エラーの削減
IDEでの優れた補完機能
リファクタリングの安全性向上
コードの自己文書化

これらのベストプラクティスを意識して、より安全で保守性の高いコードを書いていきましょう。


次回は、React×TypeScriptでの型安全なコンポーネント設計について解説します。

RK

1997年生まれ

ITエンジニア

インフラ・SRE