2 min read

TypeScript Partial, Required, and Readonly

TypeScript provides built-in utility types to transform the properties of an interface or type alias. Three of the most fundamental utilities deal with property modifiers: optionality (?) and immutability (readonly).

1. Partial<Type>

Partial<Type> constructs a type with all properties of Type set to optional. This is useful when you need to update an object but might only have a subset of the fields.

Example

interface User {
  id: number;
  name: string;
  email: string;
}

// All fields become optional: { id?: number; name?: string; email?: string; }
type PartialUser = Partial<User>;

function updateUser(user: User, fieldsToUpdate: Partial<User>) {
  return { ...user, ...fieldsToUpdate };
}

const user = { id: 1, name: "Alice", email: "alice@example.com" };
updateUser(user, { name: "Bob" }); // OK: We didn't provide 'email' or 'id'

2. Required<Type>

Required<Type> is the opposite of Partial. It constructs a type consisting of all properties of Type set to required. It removes the ? modifier from all properties.

Example

interface Config {
  host?: string;
  port?: number;
  timeout?: number;
}

// All fields become required: { host: string; port: number; timeout: number; }
type CompleteConfig = Required<Config>;

const defaultConfig: CompleteConfig = {
  host: "localhost",
  port: 8080,
  timeout: 5000
};

// Error: Property 'timeout' is missing in type...
// const badConfig: CompleteConfig = {
//   host: "localhost",
//   port: 3000
// };

3. Readonly<Type>

Readonly<Type> constructs a type with all properties of Type set to readonly. This means the properties of the constructed type cannot be reassigned.

Example

interface Todo {
  title: string;
}

const todo: Readonly<Todo> = {
  title: "Delete inactive users"
};

// todo.title = "Hello"; // Error: Cannot assign to 'title' because it is a read-only property.

Note: This is shallow immutability. If a property is an object, the properties of that nested object are still mutable unless recursively frozen.

4. How they work (Under the hood)

These types are implemented using Mapped Types.

// Partial
type Partial<T> = {
    [P in keyof T]?: T[P];
};

// Required
type Required<T> = {
    [P in keyof T]-?: T[P]; // The -? removes the optional modifier
};

// Readonly
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

programming/javascript/typescript/typescript programming/javascript/typescript/utility-types