/** * 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 extends ((...args: any[]) => any) ? T : // Preserve function types as is T extends object ? { readonly [P in keyof T]: DeepReadonly } : 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; 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}`);