Skip to content

[FEAT] Add DeepReadonly<T> type #51

@kakasoo

Description

@kakasoo

Feature Request

  • Extensions of existing features
  • Propose a type that didn't exist before

Type Expectation

A DeepReadonly<T> type is needed that recursively converts all properties of objects and arrays to readonly. Since this library provides "deep + strict" type manipulation, DeepReadonly is a natural extension.

Example Type

type Input = {
  a: number;
  b: {
    c: string;
    d: { e: boolean }[];
  };
};

type Result = DeepReadonly<Input>;
// Expected:
// {
//   readonly a: number;
//   readonly b: {
//     readonly c: string;
//     readonly d: readonly { readonly e: boolean }[];
//   };
// }

Proposed Solution

Create src/types/DeepReadonly.ts with the following rules:

  1. Add readonly modifier to all properties
  2. Convert arrays to readonly T[]
  3. Do not recurse into Date types (existing library convention)
  4. Preserve branded types (do not unbrand before processing)
  5. Add re-export to src/types/index.ts

Use Case

  • Enforce immutability on API response objects to prevent accidental mutation
  • Guarantee readonly state in Redux/Zustand store types
  • Provide type definitions for the deep version of Object.freeze

Test Requirements

All changes must include the following tests:

  1. Backward Compatibility

    • All existing tests must pass; the new type must not affect existing types
  2. Feature Verification

    • Simple object readonly conversion
    • Nested object readonly conversion
    • Array to readonly array
    • Objects inside arrays are also readonly
    • Date type preserved (not made readonly)
    • Already readonly types remain unchanged (idempotency)
  3. Complex Type Stability

    • 3+ levels of nesting ({ a: { b: { c: { d: number } } } })
    • 2D arrays ({ matrix: number[][] })
    • Arrays inside objects inside arrays ({ items: { tags: string[] }[] })
    • Tuple types ([string, { a: number }])
    • Optional property preservation ({ a?: number }{ readonly a?: number })
    • Union types ({ a: string | null })
    • Branded types ({ id: number & { __brand: 'ID' } })
    • any, never, unknown properties

How to verify:

npm run build:test && npm run test
npm run prettier

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions