hardFull Stack EngineerTechnology
What are TypeScript utility types and how do you create custom mapped types?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a tech company interview:
> "We need to make all properties of our User model optional EXCEPT 'id'. Also, we need a type that makes specific properties required. How would you build these utility types?"
> "We need to make all properties of our User model optional EXCEPT 'id'. Also, we need a type that makes specific properties required. How would you build these utility types?"
Suggested Solution
Built-in Utility Types
interface User {
id: string;
name: string;
email: string;
role: string;
}
Partial<User> // All fields optional
Required<User> // All fields required
Readonly<User> // All fields readonly
Pick<User, "id" | "name"> // Only id and name
Omit<User, "email"> // Everything except email
Record<string, User> // Dictionary of Users
Exclude<"a" | "b" | "c", "a"> // "b" | "c"
Extract<"a" | "b" | "c", "a"> // "a"
ReturnType<typeof fn> // Return type of a function
Parameters<typeof fn> // Parameters as tuple
Custom Mapped Types
// Make specific properties required
type RequireKeys<T, K extends keyof T> = T & Required<Pick<T, K>>;
// All optional EXCEPT id
type OptionalExceptId<T extends { id: string }> = Partial<Omit<T, "id">> & Pick<T, "id">;
type UpdateUser = OptionalExceptId<User>;
// { id: string; name?: string; email?: string; role?: string }
// Deep partial (nested objects)
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Make specific properties writable
type Writable<T, K extends keyof T> = Omit<T, K> & { -readonly [P in K]: T[P] };
// Builder pattern type — start empty, require fields progressively
type Builder<T, Required extends keyof T = never> = Partial<Omit<T, Required>> & Pick<T, Required>;
Real-World: Form Validation Types
// Form state: all fields are string (from inputs), validation tracks errors
type FormState<T> = {
[K in keyof T]: string;
};
type FormErrors<T> = {
[K in keyof T]?: string[];
};
function useForm<T extends Record<string, unknown>>(
initialValues: FormState<T>,
validate: (values: FormState<T>) => FormErrors<T>
) {
// Type-safe form handling
}