Syntax
Readonly<Type>Examples
Preventing Property Reassignment
Using Readonly<T> to enforce immutability at the type level.
interface Point {
x: number;
y: number;
}
const origin: Readonly<Point> = { x: 0, y: 0 };
// origin.x = 10; // Error - Cannot assign to 'x' because it is a read-only propertyReadonly Is Shallow
A common gotcha - nested objects are not automatically protected.
interface Config {
settings: { theme: string };
}
const config: Readonly<Config> = {
settings: { theme: "dark" }
};
// config.settings = { theme: "light" }; // Error - top-level property is protected
config.settings.theme = "light"; // Allowed! Readonly<T> does not protect nested objectsBest practices
- Use Readonly<T> for function parameters that should not be mutated by the function, to make that contract explicit and enforced
- Remember Readonly<T> is shallow - use a deep-readonly utility type (not built into TypeScript by default) if nested immutability is genuinely required
- Combine with as const for the most specific, fully-locked-down literal types on object literals
- Use readonly on individual array/tuple types (readonly number[]) as an alternative to wrapping the whole structure in Readonly<T>
At a glance
- Purpose
- Static types for JavaScript
- File extension
- .ts ยท .tsx
- Runs in
- Compiled to JavaScript
- Usually used with
- JavaScript and its ecosystem
Specifications & further reading
Related TypeScript documentation
Partial<T> and Required<T>
Partial<T> constructs a new type with every property of T marked optional - useful for representing partial updates, like a PATCH request body. Required<T> does the opposite, making every property mandatory even if the original type had optional ones, useful for ensuring a fully-populated object at a specific point in your code.Pick<T> and Omit<T>
Pick<T, Keys> constructs a new type by selecting only the specified properties from T. Omit<T, Keys> does the reverse, constructing a new type with all properties of T except the specified ones. Both are extremely common for deriving smaller, focused types from a larger base type without duplicating the definition.Record<K, V>
Record<Keys, ValueType> constructs an object type with a specific set of keys, all mapped to the same value type. It is the concise, standard way to type dictionary-like objects, and is often clearer than writing an equivalent index signature by hand.ReturnType<T> and Parameters<T>
ReturnType<T> extracts the return type of a function type, and Parameters<T> extracts its parameter types as a tuple. Both are especially useful for deriving types from functions you do not directly control, like third-party library functions, without manually duplicating their signatures.
Partial<T> constructs a new type with every property of T marked optional - useful for representing partial updates, like a PATCH request body. Required<T> does the opposite, making every property mandatory even if the original type had optional ones, useful for ensuring a fully-populated object at a specific point in your code.Pick<T> and Omit<T>
Pick<T, Keys> constructs a new type by selecting only the specified properties from T. Omit<T, Keys> does the reverse, constructing a new type with all properties of T except the specified ones. Both are extremely common for deriving smaller, focused types from a larger base type without duplicating the definition.Record<K, V>
Record<Keys, ValueType> constructs an object type with a specific set of keys, all mapped to the same value type. It is the concise, standard way to type dictionary-like objects, and is often clearer than writing an equivalent index signature by hand.ReturnType<T> and Parameters<T>
ReturnType<T> extracts the return type of a function type, and Parameters<T> extracts its parameter types as a tuple. Both are especially useful for deriving types from functions you do not directly control, like third-party library functions, without manually duplicating their signatures.