Readonly types describe intent, while Object.freeze protects a value at runtime.

Readonly and Freeze

freeze.ts
type Settings = {
    maxVolume: number;
    theme: string;
};

function clampVolume(value: number, settings: Readonly<Settings>): number {
    return Math.min(value, settings.maxVolume);
}

const requestedVolume: number = ;
const settings: Readonly<Settings> = Object.freeze({ maxVolume: 10, theme: "dark" });
const finalVolume: number = clampVolume(requestedVolume, settings);

console.log(`theme=${settings.theme}`);
console.log(`volume=${finalVolume}`);
type Settings = {
    maxVolume: number;
    theme: string;
};

function clampVolume(value: number, settings: Readonly<Settings>): number {
    return Math.min(value, settings.maxVolume);
}

const requestedVolume: number = ;
const settings: Readonly<Settings> = Object.freeze({ maxVolume: 10, theme: "dark" });
const finalVolume: number = clampVolume(requestedVolume, settings);

console.log(`theme=${settings.theme}`);
console.log(`volume=${finalVolume}`);
type Settings = {
    maxVolume: number;
    theme: string;
};

function clampVolume(value: number, settings: Readonly<Settings>): number {
    return Math.min(value, settings.maxVolume);
}

const requestedVolume: number = ;
const settings: Readonly<Settings> = Object.freeze({ maxVolume: 10, theme: "dark" });
const finalVolume: number = clampVolume(requestedVolume, settings);

console.log(`theme=${settings.theme}`);
console.log(`volume=${finalVolume}`);
readonly intent `Readonly<T>` prevents assignments in TypeScript, and `Object.freeze` shallowly freezes an object at runtime.