How would you model an API response with a generic type?
Define one generic interface such as ApiResponse<T> with a data: T field plus shared fields like status and message. Each endpoint then reuses it with its own data type, for example ApiResponse<User[]>.
Explain it simply
Every IRCTC-style API sends the same envelope: status, message, and a payload. Only the payload changes: a train list, a booking, a PNR status.
Instead of writing a new interface per endpoint, write the envelope once and leave the payload as a blank, T. This keeps the shared part consistent and the unique part precise. For failures, a discriminated union is even better, because the compiler knows data exists only on success.
Example
interface ApiResponse<T> { status: number; message: string; data: T; }
interface Train { number: string; name: string } type TrainList = ApiResponse<Train[]>;
const res: TrainList = { status: 200, message: "ok", data: [{ number: "12951", name: "Mumbai Rajdhani" }], };
// Stricter: success and failure as a union type Result<T> = | { ok: true; data: T } | { ok: false; error: string };
function show(r: Result<Train[]>) { if (r.ok) console.log(r.data.length); // data only exists here else console.log(r.error); }
What to say in the interview
I write the shared envelope once as a generic interface, ApiResponse<T>, and plug the payload type in per endpoint. For error handling I prefer a Result<T> discriminated union so the compiler forces me to check ok before touching data.
Common mistakes
- Typing data as any on the envelope, which throws away the whole point.