- name
- typing-generic-responses
- description
- Defines generic types for cross-boundary data: ApiResponse<T>, Paginated<T>, Result<T,E>, Repository<T,ID>. Use when designing HTTP responses, use-case return types, or persistence ports.
# Typing Generic Responses
## When to use
- Designing HTTP response shapes
- Defining use-case return types
- Creating persistence ports (Repository interfaces)
## Core rules
1. `ApiResponse<T>` for all HTTP responses (success + error envelope)
2. `Result<T,E>` for use-case outputs (typed success/error without throwing)
3. `Paginated<T>` for list endpoint responses
4. `Repository<T,ID>` generic interface for persistence ports
5. All cross-boundary types are explicit, no `any` or `object`
## Reference shape (TypeScript)
### ApiResponse<T>
```typescript
export interface ApiResponse<T = unknown> {
readonly success: boolean;
readonly data?: T;
readonly error?: { code: string; message: string; details?: Record<string, unknown> };
readonly metadata?: { timestamp: string; traceId: string };
}
```
### Result<T,E>
```typescript
export type Result<T, E extends AppError = AppError> =
| { readonly success: true; readonly value: T }
| { readonly success: false; readonly error: E };
export const ok = <T>(value: T): Result<T, never> => ({ success: true, value });
export const err = <E extends AppError>(error: E): Result<never, E> => ({ success: false, error });
```
### Repository<T,ID>
```typescript
export interface Repository<T, ID> {
findById(id: ID): Promise<T | null>;
save(entity: T): Promise<T>;
deleteById(id: ID): Promise<boolean>;
}
```
## Examples — Do
```typescript
// Use-case returns Result
async execute(id: string): Promise<Result<User, NotFoundError>> {
const user = await this.repo.findById(id);
return user ? ok(user) : err(new NotFoundError('User', id));
}
```
## Examples — Don't
```typescript
// ❌ No typing
async findUser(id: string): Promise<any> { ... }
// ❌ Direct HTTP response from use-case
return res.status(200).json({ data: user });
```
## Checklist
- [ ] `ApiResponse<T>` used for all HTTP responses
- [ ] `Result<T,E>` used for use-case returns
- [ ] `Repository<T,ID>` generic interface defined
- [ ] No `any` in cross-boundary types
See [reference/generic-patterns.md](./reference/generic-patterns.md) for full patterns.
View on GitHub