From 01c67a040768683a56a353893205361afe8ea027 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 4 Mar 2024 17:36:26 -0700 Subject: [PATCH 1/2] breaking: make `$props()` rune non-generic --- .changeset/fair-spies-repeat.md | 5 +++++ packages/svelte/elements.d.ts | 2 +- packages/svelte/src/main/ambient.d.ts | 4 ++-- packages/svelte/src/main/public.d.ts | 2 +- packages/svelte/types/index.d.ts | 8 ++++---- .../src/routes/docs/content/01-api/02-runes.md | 4 ++-- .../src/routes/docs/content/01-api/03-snippets.md | 8 ++++---- sites/svelte-5-preview/src/routes/docs/render.js | 2 +- 8 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 .changeset/fair-spies-repeat.md diff --git a/.changeset/fair-spies-repeat.md b/.changeset/fair-spies-repeat.md new file mode 100644 index 000000000000..fc4a7dbd2f74 --- /dev/null +++ b/.changeset/fair-spies-repeat.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +breaking: make `$props()` rune non-generic diff --git a/packages/svelte/elements.d.ts b/packages/svelte/elements.d.ts index 7e3b5a7ffa99..2dff32045d59 100644 --- a/packages/svelte/elements.d.ts +++ b/packages/svelte/elements.d.ts @@ -67,7 +67,7 @@ export type ToggleEventHandler = EventHandler { // Implicit children prop every element has - // Add this here so that libraries doing `$props()` don't need a separate interface + // Add this here so that libraries doing `let { ...props }: HTMLButtonAttributes = $props()` don't need a separate interface children?: import('svelte').Snippet; // Clipboard Events diff --git a/packages/svelte/src/main/ambient.d.ts b/packages/svelte/src/main/ambient.d.ts index 9d7f1d84c14d..1cb02f9f4d16 100644 --- a/packages/svelte/src/main/ambient.d.ts +++ b/packages/svelte/src/main/ambient.d.ts @@ -172,12 +172,12 @@ declare namespace $effect { * Declares the props that a component accepts. Example: * * ```ts - * let { optionalProp = 42, requiredProp } = $props<{ optionalProp?: number; requiredProps: string}>(); + * let { optionalProp = 42, requiredProp }: { optionalProp?: number; requiredProps: string } = $props(); * ``` * * https://svelte-5-preview.vercel.app/docs/runes#$props */ -declare function $props(): T; +declare function $props(): any; /** * Inspects one or more values whenever they, or the properties they contain, change. Example: diff --git a/packages/svelte/src/main/public.d.ts b/packages/svelte/src/main/public.d.ts index aaf771e46079..180c91b0116a 100644 --- a/packages/svelte/src/main/public.d.ts +++ b/packages/svelte/src/main/public.d.ts @@ -186,7 +186,7 @@ declare const SnippetReturn: unique symbol; /** * The type of a `#snippet` block. You can use it to (for example) express that your component expects a snippet of a certain type: * ```ts - * let { banner } = $props<{ banner: Snippet<{ text: string }> }>(); + * let { banner }: { banner: Snippet<{ text: string }> } = $props(); * ``` * You can only call a snippet through the `{@render ...}` tag. */ diff --git a/packages/svelte/types/index.d.ts b/packages/svelte/types/index.d.ts index aa0156846303..06b27b06a718 100644 --- a/packages/svelte/types/index.d.ts +++ b/packages/svelte/types/index.d.ts @@ -187,7 +187,7 @@ declare module 'svelte' { /** * The type of a `#snippet` block. You can use it to (for example) express that your component expects a snippet of a certain type: * ```ts - * let { banner } = $props<{ banner: Snippet<{ text: string }> }>(); + * let { banner }: { banner: Snippet<{ text: string }> } = $props(); * ``` * You can only call a snippet through the `{@render ...}` tag. */ @@ -1879,7 +1879,7 @@ declare module 'svelte/legacy' { /** * The type of a `#snippet` block. You can use it to (for example) express that your component expects a snippet of a certain type: * ```ts - * let { banner } = $props<{ banner: Snippet<{ text: string }> }>(); + * let { banner }: { banner: Snippet<{ text: string }> } = $props(); * ``` * You can only call a snippet through the `{@render ...}` tag. */ @@ -2615,12 +2615,12 @@ declare namespace $effect { * Declares the props that a component accepts. Example: * * ```ts - * let { optionalProp = 42, requiredProp } = $props<{ optionalProp?: number; requiredProps: string}>(); + * let { optionalProp = 42, requiredProp }: { optionalProp?: number; requiredProps: string } = $props(); * ``` * * https://svelte-5-preview.vercel.app/docs/runes#$props */ -declare function $props(): T; +declare function $props(): any; /** * Inspects one or more values whenever they, or the properties they contain, change. Example: diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index 850daabb4560..c262b3b8a6d3 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -465,12 +465,12 @@ To get all properties, use rest syntax: let { a, b, c, ...everythingElse } = $props(); ``` -If you're using TypeScript, you can use type arguments: +If you're using TypeScript, you can declare the prop types: ```ts type MyProps = any; // ---cut--- -let { a, b, c, ...everythingElse } = $props(); +let { a, b, c, ...everythingElse }: MyProps = $props(); ``` Props cannot be mutated, unless the parent component uses `bind:`. During development, attempts to mutate props will result in an error. diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/03-snippets.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/03-snippets.md index 850de82f38c5..e10443f7505b 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/03-snippets.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/03-snippets.md @@ -229,11 +229,11 @@ Snippets implement the `Snippet` interface imported from `'svelte'`: + ``` @@ -246,13 +246,13 @@ We can tighten things up further by declaring a generic, so that `data` and `row + ``` diff --git a/sites/svelte-5-preview/src/routes/docs/render.js b/sites/svelte-5-preview/src/routes/docs/render.js index 66881926f957..5d44f48bad0b 100644 --- a/sites/svelte-5-preview/src/routes/docs/render.js +++ b/sites/svelte-5-preview/src/routes/docs/render.js @@ -166,7 +166,7 @@ const render_content = (filename, body) => twoslashBanner: (filename, source) => { const injected = [ `// @filename: runes.d.ts`, - `declare function $props(): T`, + `declare function $props(): any`, `declare function $state(initial: T): T`, `declare function $derived(value: T): T`, `declare const $effect: ((callback: () => void | (() => void)) => void) & { pre: (callback: () => void | (() => void)) => void };` From c19754c3f3998a133382dc760ebf9c6e0f2536a0 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 12 Mar 2024 11:02:22 -0400 Subject: [PATCH 2/2] explain why type argument was removed --- .../src/routes/docs/content/01-api/02-runes.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md index c262b3b8a6d3..f2b557066df2 100644 --- a/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md +++ b/sites/svelte-5-preview/src/routes/docs/content/01-api/02-runes.md @@ -473,6 +473,15 @@ type MyProps = any; let { a, b, c, ...everythingElse }: MyProps = $props(); ``` +> In an earlier preview, `$props()` took a type argument. This caused bugs, since in a case like this... +> +> ```ts +> // @errors: 2558 +> let { x = 42 } = $props<{ x: string }>(); +> ``` +> +> ...TypeScript [widens the type](https://www.typescriptlang.org/play?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXwBIAHGHIgZwB4AVeAXnilQE8A+ACgEoAueagbgBQgiCAzwA3vAAe9eABYATPAC+c4qQqUp03uQwwsqAOaqOnIfCsB6a-AB6AfiA) of `x` to be `string | number`, instead of erroring. + Props cannot be mutated, unless the parent component uses `bind:`. During development, attempts to mutate props will result in an error. ### What this replaces