In TypeScript, you can use as const
and Readonly
to specify types more strictly for objects and arrays. This article explains the differences and usage of these features.
Basic Task List Example
Here is an array of objects with description
and completed
properties.
const tasks = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { description: 'Finish project', completed: false, },
]
In this case, the properties of each object are mutable.
Example with as const
Using as const
makes each object treated as a literal type, making them immutable.
const tasks = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { description: 'Finish project', completed: false, },
] as const; tasks.push({description: 'Buy milk', completed: false}) // Error
tasks[0].description = 'Buy milk' // Error
The above code translates to the following type:
const tasks: readonly [{ readonly description: "Buy groceries"; readonly completed: false;
}, { readonly description: "Clean the house"; readonly completed: true;
}, { readonly description: "Finish project"; readonly completed: false;
}]
Each object is treated as part of a readonly
array, making operations like adding or removing elements impossible. Additionally, the properties of each object are readonly
, preventing any changes to their values.
For example, if there is an unintended property like below, it results in an error during type checking.
const tasks = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { descriptionnnnnnnn: 'Finish project', completed: false, },
] as const; tasks.forEach((task) => { console.log(task.description); // Error
})
Example with as const satisfies
While as const
makes the elements readonly
and literal types, as const satisfies
combines literal types with type constraints.
const tasks = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { description: 'Finish project', completed: false, },
] as const satisfies { description: string, completed: boolean }[]
The above code translates to the following type:
const tasks: [{ readonly description: "Buy groceries"; readonly completed: false;
}, { readonly description: "Clean the house"; readonly completed: true;
}, { readonly description: "Finish project"; readonly completed: false;
}]
The elements of the array themselves are readonly
, but the array itself is not. This means you can change the elements of the array but not the properties within the elements.
Using satisfies
, you ensure that each object conforms to a specific type ({ description: string, completed: boolean }
in this case).
const tasks = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { descriptionnnnnnnn: 'Finish project', // Error completed: false, },
] as const satisfies { description: string, completed: boolean }[]
Example with Readonly
Using Readonly
makes the properties of the object immutable.
const tasks: Readonly<{ description: string, completed: boolean }>[] = [ { description: 'Buy groceries', completed: false, }, { description: 'Clean the house', completed: true, }, { description: 'Finish project', completed: false, },
]; tasks.push({description: 'Buy milk', completed: false}) // OK
tasks[0].completed = true // Error
The above code translates to the following type:
const tasks: Readonly<{ description: string; completed: boolean;
}>[]
All properties of the array elements are readonly
, making them immutable. However, you can still add or remove elements from the array.
Summary
In this article, we introduced the usage and differences between as const
, as const satisfies
, and Readonly
. By using these features, you can manage types more strictly in TypeScript and prevent unintended changes.