Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Svelte 5: using bind:this in $derived #13181

Open
hyunbinseo opened this issue Sep 10, 2024 · 5 comments
Open

Svelte 5: using bind:this in $derived #13181

hyunbinseo opened this issue Sep 10, 2024 · 5 comments
Milestone

Comments

@hyunbinseo
Copy link
Contributor

Describe the bug

<script lang="ts">
  let a: HTMLParagraphElement;
  let b: HTMLParagraphElement | undefined = undefined;
</script>

<p bind:this={a} bind:this={b}></p>
// Variable 'a' is used before being assigned.ts(2454)
const a2 = $derived(a.children);

// Property 'children' does not exist on type 'never'.ts(2339)
const b2 = $derived(b?.children);
// let a2: HTMLCollection
$: a2 = a.children;

// let b2: HTMLCollection | undefined
$: b2 = b?.children;

Reproduction

See above.

Logs

No response

System Info

System:
  OS: macOS 14.6.1
  CPU: (10) arm64 Apple M1 Pro
  Memory: 665.30 MB / 16.00 GB
  Shell: 5.9 - /bin/zsh
Binaries:
  Node: 22.8.0 - ~/.local/state/fnm_multishells/8044_1728636328522/bin/node
  npm: 10.8.2 - ~/.local/state/fnm_multishells/8044_1728636328522/bin/npm
  pnpm: 9.9.0 - ~/.local/state/fnm_multishells/8044_1728636328522/bin/pnpm
Browsers:
  Chrome: 128.0.6613.120
  Edge: 128.0.2739.67
  Safari: 17.6
npmPackages:
  svelte: 5.0.0-next.244 => 5.0.0-next.244

Severity

annoyance

@paoloricciuti
Copy link
Member

If you want to use it in a derived it has to be state

<script lang="ts">
	let b: HTMLParagraphElement = $state();
	const b2 = $derived(b?.children);
	$inspect(b2);
</script>

<p bind:this={b}></p>

this works fine (and the error that you are seeing on b is just about the intersection between HTMLParagraphElement and undefined being never. So i would cheat a bit and use only HTMLParagraphElement while still using the nullish to avoid the derived throwing (it also depends on where you are using the derived...if you don't access it immediately you can also avoid the nullish.

@hyunbinseo
Copy link
Contributor Author

My bad and thank you. Maybe this should be a documentation issue?

// HTMLElement | undefined
let article = $state<HTMLElement>();

// HTMLCollection | undefined
const children = $derived(article?.children);

@paoloricciuti
Copy link
Member

My bad and thank you. Maybe this should be a documentation issue?

// HTMLElement | undefined
let article = $state<HTMLElement>();

// HTMLCollection | undefined
const children = $derived(article?.children);

What's exactly the documentation issue?

@hyunbinseo
Copy link
Contributor Author

Providing an example of bind:this and $state() in the docs will be helpful.

Since the following is valid in Svelte 5 runes, I honestly didn't thought using $state() was viable.

<script lang="ts">
  let div: HTMLDivElement;
</script>

<div bind:this={div}></div>

Going one step further, maybe forcing $state with bind:this in Svelte 6 could be an option?

In the example code, canvasElement is initially undefined, but is typed as HTMLCanvasElement.

<script>
  import { onMount } from 'svelte';

  /** @type {HTMLCanvasElement} */
  let canvasElement;

  onMount(() => {
    const ctx = canvasElement.getContext('2d');
    drawStuff(ctx);
  });
</script>

<canvas bind:this={canvasElement} />

Forcing $state will fix this problem:

<script lang="ts">
  //  HTMLDivElement | undefined
  let div = $state<HTMLDivElement>();
</script>

<div bind:this={div}></div>

@paoloricciuti
Copy link
Member

As i've said technically you don't really need $state if the div never changes...you only need it in your case because you are reading the derived immediately. For example

<script lang="ts">
  let a;
	const a2 = $derived(a.children);
</script>

<button onclick={()=>{
	console.log(a2);
}}>
	log child
</button>

<p bind:this={a}></p>

this doesn't cause any problems because you are accessing a2 when a was already set.

However i agree that we need specific documentation around this topic so i'll keep this open with the doc label

@paoloricciuti paoloricciuti added this to the 5.0 milestone Sep 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants