The Partial utility type makes all properties optional:
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }
Every property becomes optional (?).
Basic Syntax
Partial<Type>
Takes one type parameter and returns a new type with all properties optional.
Common Use Case: Update Functions
Update functions accept partial data:
interface User {
id: number;
name: string;
email: string;
age: number;
}
function updateUser(id: number, updates: Partial<User>) {
// updates can have any subset of User properties
return fetch(`/api/users/${id}`, {
method: 'PATCH',
body: JSON.stringify(updates)
});
}
// All valid
updateUser(1, { name: 'Alice' });
updateUser(1, { email: 'alice@example.com' });
updateUser(1, { name: 'Alice', age: 30 });
Form State Management
Track partially completed forms:
interface RegistrationForm {
username: string;
email: string;
password: string;
confirmPassword: string;
}
function FormComponent() {
const [formData, setFormData] = useState<Partial<RegistrationForm>>({});
function handleChange(field: keyof RegistrationForm, value: string) {
setFormData({ ...formData, [field]: value });
}
function isComplete(data: Partial<RegistrationForm>): data is RegistrationForm {
return !!(data.username && data.email && data.password && data.confirmPassword);
}
}
Configuration Objects
Merge user config with defaults:
interface Config {
theme: 'light' | 'dark';
language: string;
notifications: boolean;
autoSave: boolean;
}
const defaultConfig: Config = {
theme: 'light',
language: 'en',
notifications: true,
autoSave: true
};
function createConfig(userConfig: Partial<Config>): Config {
return { ...defaultConfig, ...userConfig };
}
const config = createConfig({ theme: 'dark' });
// { theme: 'dark', language: 'en', notifications: true, autoSave: true }
How Partial Works Internally
TypeScript implements Partial using mapped types:
type Partial<T> = {
[P in keyof T]?: T[P];
};
For each property P in T, create an optional property with the same type.
Nested Partial (Deep Partial)
Partial only affects top-level properties:
interface User {
name: string;
address: {
street: string;
city: string;
};
}
type PartialUser = Partial<User>;
// { name?: string; address?: { street: string; city: string } }
address is optional, but if provided, street and city are still required.
For deep partial, define a recursive type:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
type DeepPartialUser = DeepPartial<User>;
// { name?: string; address?: { street?: string; city?: string } }
Partial vs Optional Properties
Partial makes existing properties optional. It doesn't add properties:
interface User {
id: number;
name: string;
}
type PartialUser = Partial<User>;
// Can only have 'id' and 'name', both optional
// NOT equivalent to
interface FlexibleUser {
[key: string]: any; // Can have any properties
}
Combining with Required
Make some properties required:
interface User {
id: number;
name: string;
email: string;
bio: string;
}
type UpdateUser = Partial<User> & Required<Pick<User, 'id'>>;
// { id: number; name?: string; email?: string; bio?: string }
id is required, others optional.
Validation with Partial
Type guards verify completeness:
interface Product {
id: string;
name: string;
price: number;
}
function isCompleteProduct(data: Partial<Product>): data is Product {
return (
typeof data.id === 'string' &&
typeof data.name === 'string' &&
typeof data.price === 'number'
);
}
function saveProduct(data: Partial<Product>) {
if (isCompleteProduct(data)) {
// data is now typed as Product
database.save(data);
} else {
throw new Error('Incomplete product data');
}
}
API Request Types
interface Article {
id: string;
title: string;
content: string;
authorId: string;
publishedAt: Date;
}
// POST /articles - omit server-generated fields
type CreateArticleRequest = Omit<Article, 'id' | 'publishedAt'>;
// PATCH /articles/:id - partial update
type UpdateArticleRequest = Partial<Omit<Article, 'id'>>;
class ArticleAPI {
create(data: CreateArticleRequest) { }
update(id: string, data: UpdateArticleRequest) { }
}
Class Constructors
Accept partial data with defaults:
class User {
id: number;
name: string;
email: string;
active: boolean;
constructor(data: Partial<User> & Required<Pick<User, 'id'>>) {
this.id = data.id;
this.name = data.name ?? '';
this.email = data.email ?? '';
this.active = data.active ?? true;
}
}
const user = new User({ id: 1, name: 'Alice' });
State Reducers
Redux/reducer patterns with partial updates:
interface State {
user: User | null;
loading: boolean;
error: string | null;
}
type Action =
| { type: 'UPDATE_USER'; payload: Partial<User> }
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_ERROR'; payload: string };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'UPDATE_USER':
return {
...state,
user: state.user ? { ...state.user, ...action.payload } : null
};
case 'SET_LOADING':
return { ...state, loading: action.payload };
case 'SET_ERROR':
return { ...state, error: action.payload };
}
}
When Not to Use Partial
Don't use Partial when all fields are genuinely required:
// Bad - fields are required but typed as optional
function createUser(data: Partial<User>) {
// Runtime errors if fields missing
return database.insert(data);
}
// Good - explicit about requirements
function createUser(data: User) {
return database.insert(data);
}
Partial with Generics
function merge<T>(target: T, source: Partial<T>): T {
return { ...target, ...source };
}
const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' };
const updated = merge(user, { email: 'newemail@example.com' });
React Props Patterns
interface ComponentProps {
title: string;
onSave: () => void;
onCancel: () => void;
}
// Default props pattern
const defaultProps: Partial<ComponentProps> = {
onCancel: () => {}
};
function Component(props: ComponentProps) {
const finalProps = { ...defaultProps, ...props };
// ...
}
Further Reading
The TypeScript handbook's Partial documentation covers usage examples.
For advanced patterns, see mapped types documentation.
Matt Pocock's Total TypeScript explores utility type patterns.
Partial is one of TypeScript's most useful utility types for flexible function parameters and partial updates.
0 comments