Express や Koa を使用した Web アプリケーション開発において、TypeScript の型定義に関する問題は多くの開発者が直面する課題です。この記事では、一般的な型定義の問題とその解決策について、実践的なアプローチを解説します。
リクエストとレスポンスの型定義
Express での基本的な型定義は以下のように行います:
import { Request, Response } from 'express';
interface CustomRequest extends Request {
user?: {
id: string;
name: string;
};
}
app.get('/api/user', (req: CustomRequest, res: Response) => {
const user = req.user; // 型安全にアクセス可能
res.json(user);
});ミドルウェアでの型拡張
ミドルウェアで Request オブジェクトに新しいプロパティを追加する場合:
declare global {
namespace Express {
interface Request {
customData: string;
}
}
}
const middleware = (req: Request, res: Response, next: NextFunction) => {
req.customData = 'some data';
next();
};Koa での型定義
Koa を使用する場合の型定義:
import Koa from 'koa';
interface CustomState {
user: {
id: string;
name: string;
};
}
interface CustomContext extends Koa.Context {
state: CustomState;
}
const app = new Koa<CustomState>();
app.use(async (ctx: CustomContext) => {
const user = ctx.state.user; // 型安全にアクセス可能
ctx.body = user;
});エラーハンドリングの型定義
型安全なエラーハンドリングの実装:
class CustomError extends Error {
constructor(
public statusCode: number,
message: string
) {
super(message);
this.name = 'CustomError';
}
}
app.use((err: CustomError, req: Request, res: Response, next: NextFunction) => {
res.status(err.statusCode).json({
error: err.message
});
});ジェネリック型の活用
再利用可能なコントローラーパターン:
type Controller<T> = (req: Request<T>, res: Response) => Promise<void>;
const createUser: Controller<{ name: string }> = async (req, res) => {
const { name } = req.body;
// 型安全な実装
};型定義のベストプラクティス
1. 共通の型定義ファイルの作成
// types/index.ts
export interface User {
id: string;
name: string;
}
export interface RequestWithUser extends Request {
user?: User;
}2. 型ガードの活用
function isUser(obj: any): obj is User {
return 'id' in obj && 'name' in obj;
}3. zod や io-ts などの型バリデーションライブラリの活用
import { z } from 'zod';
const UserSchema = z.object({
id: z.string(),
name: z.string()
});
app.post('/api/user', (req: Request, res: Response) => {
const result = UserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error });
}
// 型安全な処理
});パフォーマンスと型定義
型定義は実行時のパフォーマンスに影響を与えないことを理解することが重要です。TypeScript の型は開発時のみに存在し、コンパイル後は JavaScript として実行されます。
まとめ
Express/Koa での TypeScript 型定義は、適切に実装することで以下のメリットが得られます:
- コード補完の強化
- 実行時エラーの事前検出
- メンテナンス性の向上
- チーム開発での生産性向上
型定義を効果的に活用することで、より堅牢なWebアプリケーションの開発が可能になります。