Deep Readonly Configuration Type

Make a nested configuration object deeply readonly so accidental mutation of shared settings fails at compile time rather than at runtime.

TypescriptConfig Management

Code

snippet
export type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends (infer U)[]
    ? readonly DeepReadonly<U>[]
    : T[K] extends object
      ? DeepReadonly<T[K]>
      : T[K];
};

export interface ServerConfig {
  port: number;
  db: { host: string; pool: { min: number; max: number } };
  features: string[];
}

export function freeze<T>(value: T): DeepReadonly<T> {
  const deepFreeze = (node: unknown): void => {
    if (node && typeof node === "object") {
      Object.values(node as Record<string, unknown>).forEach(deepFreeze);
      Object.freeze(node);
    }
  };
  deepFreeze(value);
  return value as DeepReadonly<T>;
}