platform icon indicating copy to clipboard operation
platform copied to clipboard

feat(signals): define SignalStore members as readonly

Open Chiorufarewerin opened this issue 1 year ago • 2 comments

PR Checklist

Please check if your PR fulfills the following requirements:

  • [x] The commit message follows our guidelines: https://github.com/ngrx/platform/blob/main/CONTRIBUTING.md#commit
  • [ ] Tests for the changes have been added (for bug fixes / features)
  • [ ] Documentation has been added / updated (for bug fixes / features)

PR Type

What kind of change does this PR introduce?

[x] Feature

What is the current behavior?

https://github.com/ngrx/platform/discussions/4712

What is the new behavior?

After these changes, the store and entities will become type-safe:

this.userStore.entities = signal([{ id: 1000, name: 'Vova' }]); // error
this.userStore.entityMap()[0] = { id: 2000, name: 'Dima' }; // error
this.userStore.entities()[0] = { id: 2000, name: 'Dima' }; // error
this.userStore.entities = signal([]); // error
this.userStore.loadAll = () => undefined; // error

Does this PR introduce a breaking change?

[x] Yes
[ ] No

If someone modifies the store or directly edits entities, they will need to fix these errors (or add just as any). However, this will help prevent future errors by enforcing type safety.

Also, I want to mention that it doesn't introduces "breaking changes" in logic, like it was with Object.freeze that was reverted https://github.com/ngrx/platform/issues/4683.

Other information

In my application, almost every type includes readonly to ensure type safety. I always know when this rule is broken—only when using the as keyword. It would be great to have similar safety in @ngrx/signals.

This PR provides the minimum type safety I want. Additionally, I suggest changing every instance like this:

SignalStoreFeature<Input, { state: {}; props: {}; methods: Methods }>

To:

{
    readonly state: {};
    readonly props: {};
    readonly methods: Methods;
  }

I can also implement this change if the idea is accepted.

Chiorufarewerin avatar Mar 01 '25 11:03 Chiorufarewerin

Deploy Preview for ngrx-io canceled.

Built without sensitive environment variables

Name Link
Latest commit fb54f1538543cb2e3b6a8ecaafbd1b5fdf5e88f1
Latest deploy log https://app.netlify.com/sites/ngrx-io/deploys/67c4b05c2d5b8e000837f732

netlify[bot] avatar Mar 01 '25 11:03 netlify[bot]

This is a breaking change and cannot be considered before v20. I'll rename the PR with a precise title and it will be revisited before v20.

Regarding non-readonly SignalStore members, they could be useful in tests, if someone wants to replace a specific prop with a mocked one:

const myStore = TestBed.inject(MyStore);
myStore.mySignal = signal(10);
// ...

I don't expect anyone to do this in production code, so let's see if introducing this breaking change will be worth it.

markostanimirovic avatar Mar 05 '25 10:03 markostanimirovic

We're going to postpone this change. To have a clean PR list, we're going to close this one.

timdeschryver avatar Jun 30 '25 10:06 timdeschryver