/** * Module: Generic high order utility functions. */ // List of disallowed object types (non-plain) // ALLOW: Date | RegExp | ArrayBuffer | DataView type DisallowedObjects = | Map | Set | WeakMap | WeakSet | Promise; // Detects any disallowed object type directly type IsDisallowedObject = T extends DisallowedObjects ? true : false; // Detects if any property in T is a function type HasFunctionProps = { [K in keyof T]: T[K] extends (...args: unknown[]) => unknown ? true : false; }[keyof T] extends true ? true : false; // Detects if any property in T is a disallowed object type HasDisallowedProps = { [K in keyof T]: IsDisallowedObject; }[keyof T] extends true ? true : false; // Detects if T is a class instance (has constructor) type IsClassInstance = T extends object ? T extends { constructor: new (...args: unknown[]) => unknown } ? true : false : false; // The final check — should the object be rejected? type IsInvalid = T extends object ? HasFunctionProps extends true ? true : HasDisallowedProps extends true ? true : IsClassInstance extends true ? true : false : false; /** * Apply readonly modifier to all properties of a SIMPLE object recursively. * only works for arrays, dictionaries and primitives. */ export type RO = IsInvalid extends true ? never : T extends readonly unknown[] ? readonly RO[] : T extends object ? { readonly [K in keyof T]: RO } : T; /** * Freeze an object. */ export function deepFreeze(obj: T): RO { // Ensure the input object is not null or undefined if (obj === null || obj === undefined) { throw new Error('Cannot freeze null or undefined'); } // Freeze the current object Object.freeze(obj); // Freeze properties recursively Object.getOwnPropertyNames(obj).forEach(prop => { const value = (obj as Record)[prop]; if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) { deepFreeze(value); } }); // Return the frozen object return obj as RO; }