Partial<T>, Required<T>, and Readonly<T> transform object shapes instantly.1. Partial<T> (Making All Properties Optional)#
Partial<T> constructs a new object type where every property of T is marked as optional (?).
Production Use Case: Patch/Update API Payloads#
When updating an entity via a REST PATCH endpoint, the client can supply a subset of fields without re-sending the entire object:
interface UserProfile {
id: string;
username: string;
email: string;
bio: string;
avatarUrl: string;
}
// 🟢 Partial<UserProfile> makes ALL properties optional!
type UpdateProfileDto = Partial<UserProfile>;
/* Inferred Type:
{
id?: string;
username?: string;
email?: string;
bio?: string;
avatarUrl?: string;
}
*/
function updateProfile(userId: string, patch: Partial<UserProfile>) {
console.log(`Updating User ${userId} with keys:`, Object.keys(patch));
}
// Valid calls:
updateProfile("usr_101", { bio: "Senior Engineer" });
updateProfile("usr_101", { email: "new@acme.com", avatarUrl: "https://img.com/a.png" });How Partial<T> is Defined Under the Hood:#
type CustomPartial<T> = {
[P in keyof T]?: T[P];
};2. Required<T> (Making All Properties Mandatory)#
Required<T> does the exact opposite of Partial<T>. It removes optional modifiers (?) from every property in T, making all properties strictly mandatory.
Production Use Case: Resolving Config Defaults#
When an application accepts an optional user configuration object, a resolution function merges default settings to guarantee that every property is populated:
interface AppOptions {
theme?: "light" | "dark";
logLevel?: "info" | "debug" | "error";
retries?: number;
}
// 🟢 Required<AppOptions> removes '?' from ALL keys!
type FullyResolvedOptions = Required<AppOptions>;
/* Inferred Type:
{
theme: "light" | "dark";
logLevel: "info" | "debug" | "error";
retries: number;
}
*/
const defaultOptions: FullyResolvedOptions = {
theme: "dark",
logLevel: "info",
retries: 3,
};
function initApp(userOpts: AppOptions): FullyResolvedOptions {
return {
...defaultOptions,
...userOpts,
};
}
const activeConfig = initApp({ theme: "light" });
// Both 'logLevel' and 'retries' are guaranteed to exist on activeConfig!
console.log(activeConfig.logLevel.toUpperCase()); // Safe!
How Required<T> is Defined Under the Hood:#
TypeScript uses the -? Mapping Modifier to explicitly strip optionality:
type CustomRequired<T> = {
[P in keyof T]-?: T[P];
};3. Readonly<T> (Freezing Property Assignments)#
Readonly<T> constructs a type where every property of T receives the readonly modifier, preventing property reassignment at compile time.
Production Use Case: Immutability in State Stores#
interface AppState {
currentUser: { id: string; name: string };
isOnline: boolean;
}
function freezeState(state: AppState): Readonly<AppState> {
return Object.freeze(state);
}
const state = freezeState({
currentUser: { id: "101", name: "Alice" },
isOnline: true,
});
// ❌ Compiler Error: Cannot assign to 'isOnline' because it is a read-only property.
// state.isOnline = false;
How Readonly<T> is Defined Under the Hood:#
type CustomReadonly<T> = {
readonly [P in keyof T]: T[P];
};4. The Shallow Limitation of Readonly<T>#
Shallow Warning: Readonly<T> is strictly shallow. It only adds readonly to the top-level keys of T. Nested object properties remain mutable unless wrapped recursively!
interface NestedState {
user: {
name: string; // Nested property!
};
}
const state: Readonly<NestedState> = {
user: { name: "Alice" },
};
// ❌ Top-level property assignment is BLOCKED:
// state.user = { name: "Bob" }; // Error! Readonly property.
// 🟢 WORKED! Nested property assignment is STILL ALLOWED by TS:
state.user.name = "Bob"; // No compiler error!
(Note: In Module 4 Ep 50, we will build a custom recursive DeepReadonly<T> type to solve this exact limitation).
Summary & Next Steps#
In this episode:
- We analyzed
Partial<T>and used it for PATCH/Update DTO payloads. - We analyzed
Required<T>and used-?to enforce fully-resolved config objects. - We analyzed
Readonly<T>for state container immutability. - We investigated the Shallow Limit of
Readonly<T>on nested objects. - We revealed the mapped type implementations (
?,-?,readonly) powering these utilities.
In Episode 34: Utility Types (Pick and Omit), we will explore how to construct sub-interfaces by selecting or excluding specific key subsets!

