hardFrontend EngineerSaaS
How do TypeScript generics work with constraints, and when would you use keyof with generics?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a SaaS company interview:
> "We're building a type-safe API client. The
> "We're building a type-safe API client. The
update function should only accept fields that exist on the model, and the return type should reflect what was requested. How would you type this?"// Should be type-safe — only allow real User fields
update<User>("users", { name: "Alice" }); // ✅
update<User>("users", { age: "hello" }); // ❌ Type error
Suggested Solution
Generics with Constraints
// Constraint: T must have an 'id' property
function update<T extends { id: string }>(table: string, data: Partial<T>): Promise<T> {
return db.update(table, data);
}
// keyof constraint: only allow keys that exist on T
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const result = {} as Pick<T, K>;
keys.forEach(key => { result[key] = obj[key]; });
return result;
}
const user = { id: "1", name: "Alice", email: "a@b.com" };
pick(user, ["name"]); // ✅ { name: string }
pick(user, ["name", "id"]); // ✅ { name: string; id: string }
pick(user, ["foo"]); // ❌ Type error — "foo" not in keyof User
Real-World: Type-Safe API Client
type Model = {
users: { id: string; name: string; email: string; role: string };
posts: { id: string; title: string; body: string; authorId: string };
};
type ApiResponse<T, K extends keyof T> = Pick<T, K>;
async function fetchModel<
M extends keyof Model,
K extends keyof Model[M]
>(model: M, id: string, fields: K[]): Promise<ApiResponse<Model[M], K>> {
const data = await db.findById(model, id);
return pick(data, fields);
}
// Usage — fully type-safe
const result = await fetchModel("users", "1", ["name", "email"]);
// result: { name: string; email: string }
Key Patterns
T extends Xkeyof TT extends keyof anyT[U extends keyof T] infer