Repository: sveltejs/svelte (JavaScript)

Issue:
svelte 5: Can't use destructuring with @const and use the value inside an event handler
### Describe the bug

```svelte
<script>
  let structured = $state({ handler() {} })
</script>

{#if structured}
  {@const { handler } = structured}

  <button onclick={() => handler()}>click me</button>
{/if}
```

Generates a corrupted component which fails with `Error: handler is not defined`
This is due to the generated code:

```js
const computed_const = $.derived(() => {
  const { handler } = structured;

  return { handler };
});

button.__click = [on_click, handler];
```
where `handler` is not available outside the lexical scope of the derived closure.


### Reproduction

[REPL](https://svelte-5-preview.vercel.app/#H4sIAAAAAAAAE1WNywrCMBBFf2UYXbRQ6L6kRb_DuKjpFIPppCQTQUL-XargY3kP53AzztZRxO6UkceFsMPjumKD8li3Ee_khLDB6FMwG1HRBLvKoFmLI4EoIRlJgSboYR9lFKoyXEeeHIWqhlyg1JpV--047-z8ExbNAPlgPEeBTwsF-n9p09QliXgGz8ZZc-tzVUM_fP_K8OKwkGrf6qA5t3YumrHBxU92tjRhJyFROZcn_W9DWAEBAAA=)

### Logs

```shell
Error: handler is not defined
```


### System Info

```shell
svelte: 5.0.0-next.40
generate: "client"
```


### Severity

blocking an upgrade


Discussion:

Patch:
diff --git a/.changeset/strong-pans-doubt.md b/.changeset/strong-pans-doubt.md
new file mode 100644
index 000000000000..484fbbee52e4
--- /dev/null
+++ b/.changeset/strong-pans-doubt.md
@@ -0,0 +1,5 @@
+---
+"svelte": patch
+---
+
+fix: correctly reference destructured derived binding in event handler
diff --git a/packages/svelte/src/compiler/phases/3-transform/client/utils.js b/packages/svelte/src/compiler/phases/3-transform/client/utils.js
index 3b4d5a6f85c3..bd3a3fd44103 100644
--- a/packages/svelte/src/compiler/phases/3-transform/client/utils.js
+++ b/packages/svelte/src/compiler/phases/3-transform/client/utils.js
@@ -523,6 +523,16 @@ function get_hoistable_params(node, context) {
 				// We need both the subscription for getting the value and the store for updating
 				params.push(b.id(binding.node.name.slice(1)));
 				params.push(b.id(binding.node.name));
+			} else if (
+				// If it's a destructured derived binding, then we can extract the derived signal reference and use that.
+				binding.expression !== null &&
+				binding.expression.type === 'MemberExpression' &&
+				binding.expression.object.type === 'CallExpression' &&
+				binding.expression.object.callee.type === 'Identifier' &&
+				binding.expression.object.callee.name === '$.get' &&
+				binding.expression.object.arguments[0].type === 'Identifier'
+			) {
+				params.push(b.id(binding.expression.object.arguments[0].name));
 			} else if (
 				// If we are referencing a simple $$props value, then we need to reference the object property instead
 				binding.kind === 'prop' &&


Test patch:
diff --git a/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/_config.js b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/_config.js
new file mode 100644
index 000000000000..0645ff4a16bc
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/_config.js
@@ -0,0 +1,15 @@
+import { test } from '../../test';
+import { log } from './log.js';
+
+export default test({
+	before_test() {
+		log.length = 0;
+	},
+
+	async test({ assert, target, window }) {
+		const btn = target.querySelector('button');
+		await btn?.click();
+
+		assert.deepEqual(log, ['works!']);
+	}
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/log.js b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/log.js
new file mode 100644
index 000000000000..d3df521f4da7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/log.js
@@ -0,0 +1,2 @@
+/** @type {any[]} */
+export const log = [];
diff --git a/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/main.svelte b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/main.svelte
new file mode 100644
index 000000000000..d4f149eb81c1
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/destructure-derived-event/main.svelte
@@ -0,0 +1,15 @@
+<script>
+	import { log } from './log.js';
+
+	let structured = $state({
+		handler() {
+			log.push('works!')
+		}
+	});
+</script>
+
+{#if structured}
+  {@const { handler } = structured}
+
+  <button onclick={() => handler()}>click me</button>
+{/if}