TypeScript Mapped Types: Transform Objects at the Type Level
Mapped types let you create new types by transforming existing ones. They're the building blocks behind Partial, Required, Readonly, and your own custom utilities.
The Syntax
// Basic mapped type: iterate over keys
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type User = { id: string; name: string; age: number };
type NullableUser = Nullable<User>;
// { id: string | null; name: string | null; age: number | null }
Built-In Utilities (All Use Mapped Types)
// How Partial is implemented
type Partial<T> = { [K in keyof T]?: T[K] };
// How Required is implemented
type Required<T> = { [K in keyof T]-?: T[K] }; // -? removes optional
// How Readonly is implemented
type Readonly<T> = { readonly [K in keyof T]: T[K] };
// How Pick is implemented
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
Practical: Form State
type FormState<T> = {
values: T;
errors: Partial<Record<keyof T, string>>;
touched: Partial<Record<keyof T, boolean>>;
};
type UserForm = FormState<{ name: string; email: string; password: string }>;
// Automatically creates typed errors and touched for each field
Conditional Mapped Types
// Only make function properties optional
type OptionalMethods<T> = {
[K in keyof T]: T[K] extends Function ? T[K] | undefined : T[K];
};
// Extract only string properties
type StringProps<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
type User = { id: number; name: string; role: string; active: boolean };
type StringUserProps = StringProps<User>;
// { name: string; role: string }
Key Remapping
// Add 'get' prefix to all keys
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type UserGetters = Getters<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number }
API Response Transformer
// Convert snake_case API response to camelCase TypeScript
type CamelCase<S extends string> =
S extends `${infer H}_${infer T}`
? `${H}${Capitalize<CamelCase<T>>}`
: S;
type CamelCaseKeys<T> = {
[K in keyof T as CamelCase<string & K>]: T[K];
};
type ApiUser = { user_id: string; first_name: string; created_at: string };
type AppUser = CamelCaseKeys<ApiUser>;
// { userId: string; firstName: string; createdAt: string }
Advanced TypeScript patterns — mapped types, conditional types, template literals — are used throughout the AI SaaS Starter Kit codebase for fully type-safe APIs.
Top comments (0)