sveltejs / svelte

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

Svelte 5: Remove `undefined` from `$state` type? #11466

Open brunnerh opened 2 weeks ago

brunnerh commented 2 weeks ago

Describe the problem

Use of $state in classes may necessitate that no initial value is set as that is provided via the constructor. The current typing will then force undefined to be part of the type which can lead to errors.

// (in class)
value = $state(); // => type is unknown
value = $state<number>(); // type is number | undefined, causing errors elsewhere
value: number = $state(); // E: "Type 'number | undefined' is not assignable to type 'number'."

Workaround:

value: number = $state() as any;

Describe the proposed solution

Potentially change type to something like:

declare function $state<T = any>(initial?: T): T;

The obvious downside is that this is less correct and could lead to errors, though $state declarations without value should be relatively rare.

11455 would also address the problem by not requiring $state to be used without the initial value.

Importance

nice to have

rChaoz commented 2 weeks ago

I definitely do agree with this one - this is how state works in most frameworks, and even how it worked before with normal let declarations. Especially useful when having to interact with the DOM directly:

<script lang="ts">
    let self = $state<HTMLDivElement>()

    function onclick(e: MouseEvent) {
        // self can't be undefined here, but the type is HTMLDivElement | undefined
        self.doSomething()
    }
</script>

<div bind:this={self} {onclick}>...</div>

I would say most people are used to writing something like the code above, regardless of framework, without any typing issues.

7nik commented 2 weeks ago
<script lang="ts">
    let self = $state<HTMLDivElement>()

    function onclick(e: MouseEvent) {
        // self can't be undefined here, but the type is HTMLDivElement | undefined
        self.doSomething()
    }

    self.doSomething(); // error - `self` is undefined
    onclick(); // error - `self` is undefined
</script>

<div bind:this={self} {onclick}>...</div>

People will make such mistakes because they are new to JS, doing it in a long file, being tired, editing unfamiliar code or code they haven't touched for months, etc. But with the current typing, TS will warn you. And you can always do if (!self) return; or self!.doSomething() to pass the error.

It's possible to achieve uninitialized states in classes, but for now, all the cases I make up are related to bad coding. I'd wait for #11455 and decide what to do.