Skip to content

Commit

Permalink
fix: relax Component type (#11929)
Browse files Browse the repository at this point in the history
The current type narrows the binding type to `""` by default, which means "no bindings on this component". While this is the common case, it makes it very cumbersome to use the `Component` type because legacy components are of type `string` and as soon as you have bindings, the type is something like `"foo" | "bar"` which _also_ is not assignable to `""` which is semantically wrong, because you should be able to assign a component that can have bindings to a type that accepts none.
The pragmatic solution is to change the binding type to allow `string`, which means someone theoretically could use bindings with a component that doesn't have bindings:
```svelte
<script>
  let component: Component<{ prop: boolean }> = IAcceptNoBindings;
</script>
<!-- allowed but should be a type error -->
<svelte:component this={component} bind:prop={foo} />
```
But this is a) rare anyway and b) can be caught at runtime

This came up in comments of #11775
  • Loading branch information
dummdidumm committed Jun 6, 2024
1 parent 08b5851 commit 5b0a843
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-laws-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte": patch
---

fix: relax `Component` type
2 changes: 1 addition & 1 deletion packages/svelte/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export class SvelteComponent<
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = ''
Bindings extends keyof Props | '' = string
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
Expand Down
2 changes: 2 additions & 0 deletions packages/svelte/tests/types/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ const functionComponent: Component<
};
functionComponent.element === HTMLElement;

const bindingIsOkayToWiden: Component<any> = functionComponent;

functionComponent(null as any, {
binding: true,
// @ts-expect-error
Expand Down
6 changes: 3 additions & 3 deletions packages/svelte/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ declare module 'svelte' {
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = ''
Bindings extends keyof Props | '' = string
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
Expand Down Expand Up @@ -1990,7 +1990,7 @@ declare module 'svelte/legacy' {
*
* */
export function createClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(options: import("svelte").ComponentConstructorOptions<Props> & {
component: import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots>> | import("svelte").Component<Props, any, "">;
component: import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots>> | import("svelte").Component<Props, any, string>;
immutable?: boolean | undefined;
hydrate?: boolean | undefined;
recover?: boolean | undefined;
Expand All @@ -2001,7 +2001,7 @@ declare module 'svelte/legacy' {
* @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5.
*
* */
export function asClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(component: import("svelte").SvelteComponent<Props, Events, Slots> | import("svelte").Component<Props, any, "">): import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots> & Exports>;
export function asClassComponent<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>, Slots extends Record<string, any>>(component: import("svelte").SvelteComponent<Props, Events, Slots> | import("svelte").Component<Props, any, string>): import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots> & Exports>;
/**
* Runs the given function once immediately on the server, and works like `$effect.pre` on the client.
*
Expand Down

0 comments on commit 5b0a843

Please sign in to comment.