Skip to content

Suggestion for readonly interface #21152

Closed as not planned
Closed as not planned

Description

@ababik

Avoid duplicating "readonly" for each property by applying it to the interface definition itself.
This is to reduce boilerplate code defining immutable objects.

Having interface where ALL properties are read only:

interface State {
    readonly prop1: string;
    readonly prop2: string;
    ...
    readonly prop22: string;
}

equals to:

readonly interface State {
    prop1: string;
    prop2: string;
    ...
    prop22: string;
}

Activity

  1. acutmore commented on Jan 12, 2018

    @acutmore
    Contributor
  2. acutmore commented on Jan 12, 2018

    @acutmore
    Contributor

    Have realised that my suggestion above has some limitations. Like using this for self-referential types

    interface Foo extends Readonly<{
        prop1: string;
        prop2: number;
        prop3: this['prop1']; // 'this' type is available only in a non-static member of a class or interface.
    }> { }
  3. aleclarson commented on Mar 8, 2019

    @aleclarson

    const interface might be nice for deep readonly (kinda like { a: { b: 1 } } as const)

  4. RomainMuller commented on Mar 13, 2019

    @RomainMuller

    I think there might be value in extending this to cover a C++ like const behavior...

    const interface Foo {
      property: string; // Implicitly readonly
      method(): void; // Cannot mutate `this`
    }
    
    interface Bar {
      const method(): void; // Guaranteed non-mutating
      mutatingMethod(): void; // Could mutate `this`
    }
    
    function baz(obj: const Bar) { // Require "obj" to not be mutated by implementation
      obj.mutatingMethod(); // 💥 illegal call of mutating method on const reference
    }

    I don't have particularly strong feelings on the const versus readonly term here... const is "nice" to me as it relates nicely to what this means in C++, which (some) people are used to.

  5. shicks commented on Dec 14, 2019

    @shicks
    Contributor

    There's a lot of discussion about various ways to apply readonly to individual properties, but nothing about readonly objects themselves. I've found that TypeScript's ReadonlyArray type is very trustworthy in preventing accidentally passing an array that shouldn't be mutated somewhere that could possibly mutate it:

    const FOO = [1, 2] as const; // "TS has my back - this should never change"
    function use(arr: Array<number>) { arr[0]++; }
    use(FOO); // error!

    But readonly properties on objects just don't have sufficient checking, nor are there any guards against passing objects to contexts that might mutate the object structure itself:

    const FOO = {x: 1} as const; // "But is it really const?"
    function use(obj: {x: number, y?: number}) { obj.x++; obj.y = 2; }
    use(FOO); // "This is fine"

    Playground Example

    Ideally a readonly {x: number} would not be allowed to be silently upcast to {x: number}, possibly not even to {readonly x: number} since the readonly modifier outside would provide additional guarantees about the readonly-ness of the object structure itself. I don't have the slightest idea how feasible this actually is.

  6. acutmore commented on Dec 16, 2019

    @acutmore
    Contributor

    Stephen Hicks (@shicks)

    Ideally a readonly {x: number} would not be allowed to be silently upcast to {x: number},

    This is already how readonly works, it isn’t silently upcast.

    The reason there is no error is your example is because {x:1} as const produces the type {x:1} rather than {readonly x:1}.
    The const modifier is only narrowing number to 1.

    EDIT: I am wrong. It is being upcast. Viewing the playground on my phone it wasn’t showing the readonly modifier

  7. shicks commented on Dec 16, 2019

    @shicks
    Contributor

    This seems like sufficient scope for its own feature request, no?

  8. lautarodragan commented on May 26, 2021

    @lautarodragan

    Love this proposal. I suspect it wouldn't be that big of a challenge to implement and the value is immense.

    At a project I'm currently working on, as a team we settled on Ashley Claymore (@acutmore)'s suggestion, but using DeepReadonly:

    declare type DeepReadonly<T> = { readonly [K in keyof T]: DeepReadonly<T[K]> };
    
    interface Interface extends DeepReadonly<{
      data: {
        prop1?: string; // this would be mutable if we extended for Readonly instead of DeepReadonly
      };
    }> {}

    This is still quite a bit of boilerplate, and the empty curly braces at the end (}> { }) aren't very elegant or intuitive.

    This would be so more readable:

    readonly interface State {
        prop1: string;
        prop2: string;
        ...
        prop22: string;
    }

    One question does arise: should the readonly modifier for the interface be a deep-readonly, or a shallow one?

    Intuitively I think a deep/recursive readonly makes the most sense.

  9. shicks commented on May 28, 2021

    @shicks
    Contributor

    I guess my point is that we don't actually gain that much by simply distributing readonly to all the fields when the implicit downcasting (I believe I misspoke earlier when I called it upcasting) is on by default. Introducing new syntax provides an opportunity to also introduce stricter defaults that would otherwise be infeasible: if the readonly interface refused to implicitly downcast, I argue it would be much safer and more useful (and would be more likely to pull its own weight) than if it were simply syntactic sugar around something that's already possible.

    EDIT: fixed typo

  10. lautarodragan commented on Jul 8, 2021

    @lautarodragan

    Ah, I see what you mean Stephen Hicks (@shicks)! I hadn't seen your playground example before. That is really bad :/

    I agree with you. I think they should be separate things though. I'd intuitively expect readonly to be syntax sugar (pretty much), but it's also a good opportunity to fix a mistake from the past.

  11. lautarodragan commented on Jul 15, 2021

    @lautarodragan

    Stephen Hicks (@shicks) is there a GH issue to specifically tackle the downcasting?

  12. shicks commented on Jul 15, 2021

    @shicks
    Contributor

    I'm not aware of one.

  13. acutmore commented on Jul 15, 2021

    @acutmore
    Contributor

    Stephen Hicks (@shicks) is there a GH issue to specifically tackle the downcasting?

    I'm not aware of one.

    #13347

  14. RyanCavanaugh commented on Oct 20, 2025

    @RyanCavanaugh
    Member

    Les track this at #45535

  15. added
    DuplicateAn existing issue was already created
    and removed
    SuggestionAn idea for TypeScript
    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this feature
    on Oct 20, 2025
  16. typescript-bot commented on Oct 23, 2025

    @typescript-bot
    Contributor

    This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  17. locked as resolved and limited conversation to collaborators on Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions