sveltejs / svelte

Cybernetically enhanced web apps
https://svelte.dev
MIT License
76.8k stars 3.98k forks source link

feat: allow state and derived declarations inside class constructors #11455

Open dummdidumm opened 2 weeks ago

dummdidumm commented 2 weeks ago

This is a POC to test the feasability of allowing $state and $derived inside the constructor. Some cases still missing, and validation needs to adjusted to allow this.x = $state(0) inside the constructor but only at the top level of it.

Uncanney-valley-wise I think it's fine. TypeScript for example didn't have the ability to know that something was definitely initialized unless you either directly declared it or did so in the constructor (it did not follow method invocations inside the constructor) and it was honestly fine. I think the rule of only allowing it inside the constructor at the top level (i.e. not nested in an if block) is understandable and intuitive.

I'll proceed with this once we agreed on whether or not we want to do this.

closes #11116 closes #11339

Before submitting the PR, please make sure you do the following

Tests and linting

changeset-bot[bot] commented 2 weeks ago

🦋 Changeset detected

Latest commit: d45c8f066222c1dc268529750d20242ea363262f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package | Name | Type | | ------ | ----- | | svelte | Patch |

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Rich-Harris commented 1 week ago

i'm in two minds. it definitely makes certain derived scenarios easier, but it does reduce clarity if people get into the habit of just doing this stuff in the constructor instead of declaring state fields. i also think it gets a little tricky around enumeration. it's one thing to say 'state fields are turned into accessors on the prototype, which are not enumerated on the instance'

(by which i mean this)

class Foo {
  a = 1;
  get b() { return 2 }
}

var foo = new Foo(); // { a: 1 }

but it's something else if you're doing something that very much looks like assigning to a property on the instance