From a3bc7d5698425ec9dde86eb302f2fd56d9da8f96 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 24 Nov 2023 16:34:34 +0100 Subject: [PATCH] fix: type-level back-compat for default slot and children prop If someone has an existing SvelteComponent type definition and using the slot generic to type the default slot, automatically add that slot to the prop type as "children" --- .changeset/wet-games-fly.md | 5 +++++ packages/svelte/src/main/public.d.ts | 12 ++++++++++-- packages/svelte/tests/types/component.ts | 18 ++++++++++++------ 3 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 .changeset/wet-games-fly.md diff --git a/.changeset/wet-games-fly.md b/.changeset/wet-games-fly.md new file mode 100644 index 0000000000..9f03e26882 --- /dev/null +++ b/.changeset/wet-games-fly.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: type-level back-compat for default slot and children prop diff --git a/packages/svelte/src/main/public.d.ts b/packages/svelte/src/main/public.d.ts index 63f5122bbe..5849835ea2 100644 --- a/packages/svelte/src/main/public.d.ts +++ b/packages/svelte/src/main/public.d.ts @@ -18,6 +18,14 @@ export interface ComponentConstructorOptions< $$inline?: boolean; } +// Utility type for ensuring backwards compatibility on a type level: If there's a default slot, add 'children' to the props if it doesn't exist there already +type PropsWithChildren = Props & + (Props extends { children?: any } + ? {} + : Slots extends { default: any } + ? { children?: Snippet } + : {}); + /** * Can be used to create strongly typed Svelte components. * @@ -57,13 +65,13 @@ export class SvelteComponent< * is a stop-gap solution. Migrate towards using `mount` or `createRoot` instead. See * https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes for more info. */ - constructor(options: ComponentConstructorOptions); + constructor(options: ComponentConstructorOptions>); /** * For type checking capabilities only. * Does not exist at runtime. * ### DO NOT USE! * */ - $$prop_def: Props; + $$prop_def: PropsWithChildren; /** * For type checking capabilities only. * Does not exist at runtime. diff --git a/packages/svelte/tests/types/component.ts b/packages/svelte/tests/types/component.ts index 749c648e2f..13ebb65753 100644 --- a/packages/svelte/tests/types/component.ts +++ b/packages/svelte/tests/types/component.ts @@ -15,11 +15,11 @@ class LegacyComponent extends SvelteComponent< { slot: { slotProps: boolean } } > {} -// @ts-expect-error const legacyComponent = new LegacyComponent({ target: null as any as Document | Element | ShadowRoot, props: { prop: 'foo', + // @ts-expect-error x: '' } }); @@ -56,14 +56,20 @@ class NewComponent extends SvelteComponent< anExport: string = ''; } -// @ts-expect-error new NewComponent({ - prop: 'foo', - x: '' + target: null as any, + props: { + prop: 'foo', + // @ts-expect-error + x: '' + } }); const newComponent: NewComponent = new NewComponent({ - prop: 'foo' + target: null as any, + props: { + prop: 'foo' + } }); newComponent.$$events_def.event; // @ts-expect-error @@ -123,11 +129,11 @@ instance.anExport === 1; // --------------------------------------------------------------------------- interop const AsLegacyComponent = asClassComponent(newComponent); -// @ts-expect-error new AsLegacyComponent({ target: null as any, props: { prop: '', + // @ts-expect-error x: '' } });