Readonly<Product> is Product with every property marked readonly. Assigning to one is TS2540 ("Cannot assign to 'price' because it is a read-only property"), as the first example shows. On a parameter, it tells callers that the function will not change the object they pass.
TypeScript has had Readonly since version 2.1. It exists only for TypeScript's checker; Object.freeze is the run-time counterpart and returns a Readonly type.
At a glance
| JSDoc in a .js file | TypeScript in a .ts file |
|---|---|
/** @type {Readonly<Product>} */ | : Readonly<Product> |
Examples
/** @typedef {{ sku: string, price: number, tags: string[] }} Product */
/** @param {Readonly<Product>} product */
export function discounted(product) {
product.price = product.price * 0.8;error TS2540: Cannot assign to 'price' because it is a read-only property.
return product;
}type Product = { sku: string; price: number; tags: string[] };
export function discounted(product: Readonly<Product>) {
product.price = product.price * 0.8;error TS2540: Cannot assign to 'price' because it is a read-only property.
return product;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ sku: string, tags: string[] }} Product */
/** @param {Readonly<Product>} product */
export function tagSale(product) {
// Allowed: Readonly covers product.tags, not the array it points to.
product.tags.push("sale");
return product;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Only one level deep
Readonly covers the object's own properties, not the objects they point to. In the second example product.tags.push("sale") passes, because tags itself was not reassigned. Type nested arrays as readonly string[] when they should not change either.
Gotchas
- A
Readonly<T>value can be passed where a plainTis expected; TypeScript does not treatreadonlyproperties as incompatible with mutable ones in assignments. - For arrays,
Readonly<string[]>is the same asreadonly string[].
Questions
How do I make an object parameter read-only in JSDoc?
Type it @param {Readonly<Product>} product. Nested arrays and objects need their own readonly types.
Sources
- Utility Types: Readonly<Type> · TypeScript handbook
Last reviewed 20 October 2026.