Create a Deeply Immutable (DeepReadonly) Type
Owner: SnippetBot
Created: 2026-07-15 00:00:50
Size: 2.18 KB
Expires: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
/**
* Creates a type where all properties, including those in nested objects and arrays,
* are marked as `readonly`. This ensures deep immutability, preventing accidental
* modification of object structures after they are created.
*/
type DeepReadonly<T> =
T extends ((...args: any[]) => any) ? T : // Preserve function types as is
T extends object ? { readonly [P in keyof T]: DeepReadonly<T[P]> } :
T;
interface Coordinate {
x: number;
y: number;
}
interface UserConfig {
theme: 'light' | 'dark';
notifications: {
email: boolean;
sms: boolean;
};
preferences: {
language: string;
timezone: string;
coordinates?: Coordinate[]; // Array of objects
};
plugins?: string[]; // Array of primitives
logActivity: () => void; // A function
}
type ImmutableUserConfig = DeepReadonly<UserConfig>;
const defaultConfig: ImmutableUserConfig = {
theme: 'dark',
notifications: {
email: true,
sms: false,
},
preferences: {
language: 'en-US',
timezone: 'UTC',
coordinates: [
{ x: 10, y: 20 },
{ x: 30, y: 40 },
],
},
plugins: ['analytics', 'logger'],
logActivity: () => console.log('Activity logged.'),
};
console.log('--- DeepReadonly Demo ---');
console.log('Original config:', JSON.stringify(defaultConfig, null, 2));
// Attempting to modify properties will result in a type error:
// defaultConfig.theme = 'light'; // Type Error: Cannot assign to 'theme' because it is a read-only property.
// defaultConfig.notifications.email = false; // Type Error: Cannot assign to 'email' because it is a read-only property.
// defaultConfig.preferences.coordinates.push({ x: 50, y: 60 }); // Type Error: Property 'push' does not exist on type 'readonly Coordinate[]'.
// defaultConfig.plugins[0] = 'new-plugin'; // Type Error: Index signature in type 'readonly string[]' only permits reading.
// Function properties remain callable but the function itself cannot be reassigned.
defaultConfig.logActivity();
// defaultConfig.logActivity = () => {}; // Type Error: Cannot assign to 'logActivity' because it is a read-only property.
const readOnlyX = defaultConfig.preferences.coordinates?.[0].x;
console.log(`Read-only coordinate x: ${readOnlyX}`);