top | item 43299394

(no title)

teekoiv | 11 months ago

I very much agree with the overall thesis of the post. Runes are just irksome in subtle ways.

One big point that the post misses, is that the Class escape hatch for runes is incompatible with constructor-set parameters.

Say you have a class that wraps a HTMLElement which you set in the constructor. This doesn't work:

  class Wrapper {
    dom: HTMLElement = $state()
    constructor(el: HTMLElement) {
      this.dom = el
    }
  }
as TypeScript throws an error about `Type 'undefined' is not assignable to type 'HTMLElement' for the $state()`. You could fix it by eg. `$state(undefined as unknown as HTMLElement)` but that's dumb. Interesting enough you could do something like:

  class Wrapper {
    dom: HTMLElement
    constructor(el: HTMLElement) {
      let d = $state(el)
      this.dom = d
    }
  }
Moreover, Vite/esbuild mangles class-field parameters with esnext into constructor-set parameters as they are just more versatile. So the original code becomes something like:

  class Wrapper {
    constructor(el: HTMLElement) {
      this.dom = $state(el)
    }
  }
Which is incompatible with rules of runes. I did whine about this already https://github.com/sveltejs/svelte/issues/14600 but so far no clear answer

discuss

order

_benton|11 months ago

I agree with everything in the post but I still like using Svelte. We've adopted our own Store class and decorators which eliminates some of the issues in the post.

    class Wrapper extends Store {
        @state() accessor dom: HTMLElement;
        constructor(el: HTMLElement) { this.dom = el; }
    }
The advantages here are that a) we don't need the .svelte.ts postfix and b) @state() makes TS support flawless with runes. And since we only use classes for state, most of the other objections in the post are mitigated. On the bindable issue, we simply just don't use that feature - one way dataflow ftw! :)

I'm not saying Svelte 5 is flawless at all, but I think we found an approach that minimizes the downsides. The upside is really good performance and a metaframework that makes the most sense out of all the current options.

braebo|11 months ago

Oh wow, so this still works even though Svelte doesn't support TypeScript features that aren't just type annotations (like decorators)? Is that because you're outside of `svelte.ts` files so the compiler never touches them?

beremaki|11 months ago

> You could fix it by eg. `$state(undefined as unknown as HTMLElement)` but that's dumb

I think the generic is what you are looking for

  class Wrapper {
    dom = $state<HTMLElement>();
    constructor(el: HTMLElement) {
      this.dom = el;
    }
  }
the dom property will still be HTMLElement | undefined, if the 'undefined' bothers you have to add an exclamation mark and write "$state<HTMLElement>()!"

Etheryte|11 months ago

That's still a manual type assertion though, and if a regular usage pattern demands one, then the library is doing it wrong. Regardless of how you annotate it, every manual override reduces the effectiveness of the type system.

hmry|11 months ago

Use $state()! to fix the undefined is not assignable error.