feat: provide new `Component` type that represents the shape of components (#11775)

In Svelte 3 and 4, components were classes under the hood, and the base class was `SvelteComponent`. This class was also used in language tools to properly type check the template code.
In Svelte 5, components are functions. To give people a way to extend them programmatically, it would be good to expose the actual shape of components. This is why this PR introduces a new `Component` type.
For backwards compatibility reasons, we can't just get rid of the old class-based types. We also need to ensure that language tools can work with both the new and old types: There are many libraries out there that provide `d.ts` files with type definitions written using the class types - these should not error.
That's why there's an accompagning language tools PR (https://github.com/sveltejs/language-tools/pull/2380) that's doing the heavy lifting: Instead of generating classes, it now generates a constant and an interfaces and uses Typescript's declaration merging feature to provide both so we can declare a component export as being both a class and a function. That ensures that people can still instantiate them with `new` (which they can do if they use the `legacy.componentApi` compiler option), and it also ensure we don't need to adjust any other code generation mechanisms in language tools yet - from a language tools perspective, classes are still the norm. But through exposing the default export as being _also_ callable as a function we can in a future Svelte version, where classes/the Svelte 4 syntax are removed completely, seamlessly switch over to using functions in the code generation, too, and the `d.ts` files generated up until that point will support it because of the dual shape. This way we have both backwards and forwards compatibility.
pull/11800/head
Simon H 1 year ago committed by GitHub
parent 6e8131d826
commit 8c66b64e6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
"svelte": patch
---
feat: provide `Component` type that represents the new shape of Svelte components

@ -3,7 +3,7 @@
import './ambient.js'; import './ambient.js';
/** /**
* @deprecated Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. * @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions.
* Use `mount` or `createRoot` instead to instantiate components. * Use `mount` or `createRoot` instead to instantiate components.
* See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes)
* for more info. * for more info.
@ -34,32 +34,10 @@ type Properties<Props, Slots> = Props &
: {}); : {});
/** /**
* Can be used to create strongly typed Svelte components.
*
* #### Example:
*
* You have component library on npm called `component-library`, from which
* you export a component called `MyComponent`. For Svelte+TypeScript users,
* you want to provide typings. Therefore you create a `index.d.ts`:
* ```ts
* import { SvelteComponent } from "svelte";
* export class MyComponent extends SvelteComponent<{foo: string}> {}
* ```
* Typing this makes it possible for IDEs like VS Code with the Svelte extension
* to provide intellisense and to use the component like this in a Svelte file
* with TypeScript:
* ```svelte
* <script lang="ts">
* import { MyComponent } from "component-library";
* </script>
* <MyComponent foo={'bar'} />
* ```
*
* This was the base class for Svelte components in Svelte 4. Svelte 5+ components * This was the base class for Svelte components in Svelte 4. Svelte 5+ components
* are completely different under the hood. You should only use this type for typing, * are completely different under the hood. For typing, use `Component` instead.
* not actually instantiate components with `new` - use `mount` or `createRoot` instead. * To instantiate components, use `mount` or `createRoot`.
* See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) * See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more info.
* for more info.
*/ */
export class SvelteComponent< export class SvelteComponent<
Props extends Record<string, any> = Record<string, any>, Props extends Record<string, any> = Record<string, any>,
@ -80,27 +58,25 @@ export class SvelteComponent<
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */ */
$$prop_def: Props; // Without Properties: unnecessary, causes type bugs $$prop_def: Props; // Without Properties: unnecessary, causes type bugs
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */
* */
$$events_def: Events; $$events_def: Events;
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */
* */
$$slot_def: Slots; $$slot_def: Slots;
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */ */
$$bindings?: string; $$bindings?: string;
/** /**
@ -129,7 +105,61 @@ export class SvelteComponent<
} }
/** /**
* @deprecated Use `SvelteComponent` instead. See TODO for more information. * Can be used to create strongly typed Svelte components.
*
* #### Example:
*
* You have component library on npm called `component-library`, from which
* you export a component called `MyComponent`. For Svelte+TypeScript users,
* you want to provide typings. Therefore you create a `index.d.ts`:
* ```ts
* import { Component } from "svelte";
* export declare const MyComponent: Component<{ foo: string }> {}
* ```
* Typing this makes it possible for IDEs like VS Code with the Svelte extension
* to provide intellisense and to use the component like this in a Svelte file
* with TypeScript:
* ```svelte
* <script lang="ts">
* import { MyComponent } from "component-library";
* </script>
* <MyComponent foo={'bar'} />
* ```
*/
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = ''
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
* @param props The props passed to the component.
*/
(
internal: unknown,
props: Props
): {
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes
* for more info.
*/
$on?(type: string, callback: (e: any) => void): () => void;
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes
* for more info.
*/
$set?(props: Partial<Props>): void;
} & Exports;
/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */
element?: typeof HTMLElement;
/** Does not exist at runtime, for typing capabilities only. DO NOT USE */
z_$$bindings?: Bindings;
}
/**
* @deprecated Use `Component` instead. See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more information.
*/ */
export class SvelteComponentTyped< export class SvelteComponentTyped<
Props extends Record<string, any> = Record<string, any>, Props extends Record<string, any> = Record<string, any>,
@ -138,6 +168,8 @@ export class SvelteComponentTyped<
> extends SvelteComponent<Props, Events, Slots> {} > extends SvelteComponent<Props, Events, Slots> {}
/** /**
* @deprecated The new `Component` type does not have a dedicated Events type. Use `ComponentProps` instead.
*
* Convenience type to get the events the given component expects. Example: * Convenience type to get the events the given component expects. Example:
* ```html * ```html
* <script lang="ts"> * <script lang="ts">
@ -166,10 +198,16 @@ export type ComponentEvents<Comp extends SvelteComponent> =
* </script> * </script>
* ``` * ```
*/ */
export type ComponentProps<Comp extends SvelteComponent> = export type ComponentProps<Comp extends SvelteComponent | Component> =
Comp extends SvelteComponent<infer Props> ? Props : never; Comp extends SvelteComponent<infer Props>
? Props
: Comp extends Component<infer Props>
? Props
: never;
/** /**
* @deprecated This type is obsolete when working with the new `Component` type.
*
* Convenience type to get the type of a Svelte component. Useful for example in combination with * Convenience type to get the type of a Svelte component. Useful for example in combination with
* dynamic components using `<svelte:component>`. * dynamic components using `<svelte:component>`.
* *

@ -84,13 +84,12 @@ export function stringify(value) {
* *
* @template {Record<string, any>} Props * @template {Record<string, any>} Props
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @template {Record<string, any>} Events * @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props, Events>>} component
* @param {{ * @param {{
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* anchor?: Node; * anchor?: Node;
* props?: Props; * props?: Props;
* events?: { [Property in keyof Events]: (e: Events[Property]) => any }; * events?: Record<string, (e: any) => any>;
* context?: Map<any, any>; * context?: Map<any, any>;
* intro?: boolean; * intro?: boolean;
* }} options * }} options
@ -111,12 +110,11 @@ export function mount(component, options) {
* *
* @template {Record<string, any>} Props * @template {Record<string, any>} Props
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @template {Record<string, any>} Events * @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props>> | import('../../index.js').Component<Props, Exports, any>} component
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<Props, Events>>} component
* @param {{ * @param {{
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* props?: Props; * props?: Props;
* events?: { [Property in keyof Events]: (e: Events[Property]) => any }; * events?: Record<string, (e: any) => any>;
* context?: Map<any, any>; * context?: Map<any, any>;
* intro?: boolean; * intro?: boolean;
* recover?: boolean; * recover?: boolean;
@ -181,7 +179,7 @@ export function hydrate(component, options) {
/** /**
* @template {Record<string, any>} Exports * @template {Record<string, any>} Exports
* @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<any>>} Component * @param {import('../../index.js').ComponentType<import('../../index.js').SvelteComponent<any>> | import('../../index.js').Component<any>} Component
* @param {{ * @param {{
* target: Document | Element | ShadowRoot; * target: Document | Element | ShadowRoot;
* anchor: Node; * anchor: Node;

@ -14,7 +14,7 @@ import { define_property } from '../internal/client/utils.js';
* @template {Record<string, any>} Slots * @template {Record<string, any>} Slots
* *
* @param {import('svelte').ComponentConstructorOptions<Props> & { * @param {import('svelte').ComponentConstructorOptions<Props> & {
* component: import('svelte').ComponentType<import('svelte').SvelteComponent<Props, Events, Slots>>; * component: import('svelte').ComponentType<import('svelte').SvelteComponent<Props, Events, Slots>> | import('svelte').Component<Props>;
* immutable?: boolean; * immutable?: boolean;
* hydrate?: boolean; * hydrate?: boolean;
* recover?: boolean; * recover?: boolean;
@ -36,7 +36,7 @@ export function createClassComponent(options) {
* @template {Record<string, any>} Events * @template {Record<string, any>} Events
* @template {Record<string, any>} Slots * @template {Record<string, any>} Slots
* *
* @param {import('svelte').SvelteComponent<Props, Events, Slots>} component * @param {import('svelte').SvelteComponent<Props, Events, Slots> | import('svelte').Component<Props>} component
* @returns {import('svelte').ComponentType<import('svelte').SvelteComponent<Props, Events, Slots> & Exports>} * @returns {import('svelte').ComponentType<import('svelte').SvelteComponent<Props, Events, Slots> & Exports>}
*/ */
export function asClassComponent(component) { export function asClassComponent(component) {

@ -5,7 +5,8 @@ import {
type ComponentProps, type ComponentProps,
type ComponentType, type ComponentType,
mount, mount,
hydrate hydrate,
type Component
} from 'svelte'; } from 'svelte';
SvelteComponent.element === HTMLElement; SvelteComponent.element === HTMLElement;
@ -49,6 +50,15 @@ const legacyComponentEvents2: ComponentEvents<LegacyComponent> = {
event: new KeyboardEvent('click') event: new KeyboardEvent('click')
}; };
const legacyComponentInstance: SvelteComponent<{ prop: string }> = new LegacyComponent({
target: null as any as Document | Element | ShadowRoot,
props: {
prop: 'foo'
}
});
const legacyComponentClass: typeof SvelteComponent<{ prop: string }> = LegacyComponent;
// --------------------------------------------------------------------------- new: functions // --------------------------------------------------------------------------- new: functions
class NewComponent extends SvelteComponent< class NewComponent extends SvelteComponent<
@ -130,7 +140,7 @@ hydrate(NewComponent, {
}, },
events: { events: {
event: (e) => event: (e) =>
// @ts-expect-error // we're not type checking this as it's an edge case and removing the generic later would be an annoying mini breaking change
e.doesNotExist e.doesNotExist
}, },
immutable: true, immutable: true,
@ -174,3 +184,73 @@ const x: typeof asLegacyComponent = createClassComponent({
hydrate: true, hydrate: true,
component: NewComponent component: NewComponent
}); });
// --------------------------------------------------------------------------- function component
const functionComponent: Component<
{ binding: boolean; readonly: string },
{ foo: 'bar' },
'binding'
> = (a, props) => {
props.binding === true;
props.readonly === 'foo';
// @ts-expect-error
props.readonly = true;
// @ts-expect-error
props.binding = '';
return {
foo: 'bar'
};
};
functionComponent.element === HTMLElement;
functionComponent(null as any, {
binding: true,
// @ts-expect-error
readonly: true
});
const functionComponentInstance = functionComponent(null as any, {
binding: true,
readonly: 'foo',
// @ts-expect-error
x: ''
});
functionComponentInstance.foo === 'bar';
// @ts-expect-error
functionComponentInstance.foo = 'foo';
mount(functionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
readonly: 'foo',
// would be nice to error here, probably needs NoInfer type helper in upcoming TS 5.5
x: ''
}
});
mount(functionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
// @ts-expect-error wrong type
readonly: 1
}
});
hydrate(functionComponent, {
target: null as any as Document | Element | ShadowRoot,
props: {
binding: true,
readonly: 'foo',
// would be nice to error here, probably needs NoInfer type helper in upcoming TS 5.5
x: ''
}
});
hydrate(functionComponent, {
target: null as any as Document | Element | ShadowRoot,
// @ts-expect-error missing prop
props: {
binding: true
}
});

@ -1,6 +1,6 @@
declare module 'svelte' { declare module 'svelte' {
/** /**
* @deprecated Svelte components were classes in Svelte 4. In Svelte 5, thy are not anymore. * @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions.
* Use `mount` or `createRoot` instead to instantiate components. * Use `mount` or `createRoot` instead to instantiate components.
* See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) * See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes)
* for more info. * for more info.
@ -31,32 +31,10 @@ declare module 'svelte' {
: {}); : {});
/** /**
* Can be used to create strongly typed Svelte components.
*
* #### Example:
*
* You have component library on npm called `component-library`, from which
* you export a component called `MyComponent`. For Svelte+TypeScript users,
* you want to provide typings. Therefore you create a `index.d.ts`:
* ```ts
* import { SvelteComponent } from "svelte";
* export class MyComponent extends SvelteComponent<{foo: string}> {}
* ```
* Typing this makes it possible for IDEs like VS Code with the Svelte extension
* to provide intellisense and to use the component like this in a Svelte file
* with TypeScript:
* ```svelte
* <script lang="ts">
* import { MyComponent } from "component-library";
* </script>
* <MyComponent foo={'bar'} />
* ```
*
* This was the base class for Svelte components in Svelte 4. Svelte 5+ components * This was the base class for Svelte components in Svelte 4. Svelte 5+ components
* are completely different under the hood. You should only use this type for typing, * are completely different under the hood. For typing, use `Component` instead.
* not actually instantiate components with `new` - use `mount` or `createRoot` instead. * To instantiate components, use `mount` or `createRoot`.
* See [breaking changes](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) * See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more info.
* for more info.
*/ */
export class SvelteComponent< export class SvelteComponent<
Props extends Record<string, any> = Record<string, any>, Props extends Record<string, any> = Record<string, any>,
@ -77,27 +55,25 @@ declare module 'svelte' {
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */ */
$$prop_def: Props; // Without Properties: unnecessary, causes type bugs $$prop_def: Props; // Without Properties: unnecessary, causes type bugs
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */
* */
$$events_def: Events; $$events_def: Events;
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */
* */
$$slot_def: Slots; $$slot_def: Slots;
/** /**
* For type checking capabilities only. * For type checking capabilities only.
* Does not exist at runtime. * Does not exist at runtime.
* ### DO NOT USE! * ### DO NOT USE!
* */ */
$$bindings?: string; $$bindings?: string;
/** /**
@ -126,7 +102,61 @@ declare module 'svelte' {
} }
/** /**
* @deprecated Use `SvelteComponent` instead. See TODO for more information. * Can be used to create strongly typed Svelte components.
*
* #### Example:
*
* You have component library on npm called `component-library`, from which
* you export a component called `MyComponent`. For Svelte+TypeScript users,
* you want to provide typings. Therefore you create a `index.d.ts`:
* ```ts
* import { Component } from "svelte";
* export declare const MyComponent: Component<{ foo: string }> {}
* ```
* Typing this makes it possible for IDEs like VS Code with the Svelte extension
* to provide intellisense and to use the component like this in a Svelte file
* with TypeScript:
* ```svelte
* <script lang="ts">
* import { MyComponent } from "component-library";
* </script>
* <MyComponent foo={'bar'} />
* ```
*/
export interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = ''
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
* @param props The props passed to the component.
*/
(
internal: unknown,
props: Props
): {
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes
* for more info.
*/
$on?(type: string, callback: (e: any) => void): () => void;
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes
* for more info.
*/
$set?(props: Partial<Props>): void;
} & Exports;
/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */
element?: typeof HTMLElement;
/** Does not exist at runtime, for typing capabilities only. DO NOT USE */
z_$$bindings?: Bindings;
}
/**
* @deprecated Use `Component` instead. See [breaking changes documentation](https://svelte-5-preview.vercel.app/docs/breaking-changes#components-are-no-longer-classes) for more information.
*/ */
export class SvelteComponentTyped< export class SvelteComponentTyped<
Props extends Record<string, any> = Record<string, any>, Props extends Record<string, any> = Record<string, any>,
@ -135,6 +165,8 @@ declare module 'svelte' {
> extends SvelteComponent<Props, Events, Slots> {} > extends SvelteComponent<Props, Events, Slots> {}
/** /**
* @deprecated The new `Component` type does not have a dedicated Events type. Use `ComponentProps` instead.
*
* Convenience type to get the events the given component expects. Example: * Convenience type to get the events the given component expects. Example:
* ```html * ```html
* <script lang="ts"> * <script lang="ts">
@ -163,10 +195,16 @@ declare module 'svelte' {
* </script> * </script>
* ``` * ```
*/ */
export type ComponentProps<Comp extends SvelteComponent> = export type ComponentProps<Comp extends SvelteComponent | Component> =
Comp extends SvelteComponent<infer Props> ? Props : never; Comp extends SvelteComponent<infer Props>
? Props
: Comp extends Component<infer Props>
? Props
: never;
/** /**
* @deprecated This type is obsolete when working with the new `Component` type.
*
* Convenience type to get the type of a Svelte component. Useful for example in combination with * Convenience type to get the type of a Svelte component. Useful for example in combination with
* dynamic components using `<svelte:component>`. * dynamic components using `<svelte:component>`.
* *
@ -310,11 +348,11 @@ declare module 'svelte' {
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
* *
* */ * */
export function mount<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, Events, any>>, options: { export function mount<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, any, any>> | Component<Props, Exports, any>, options: {
target: Document | Element | ShadowRoot; target: Document | Element | ShadowRoot;
anchor?: Node | undefined; anchor?: Node | undefined;
props?: Props | undefined; props?: Props | undefined;
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined; events?: Record<string, (e: any) => any> | undefined;
context?: Map<any, any> | undefined; context?: Map<any, any> | undefined;
intro?: boolean | undefined; intro?: boolean | undefined;
}): Exports; }): Exports;
@ -322,10 +360,10 @@ declare module 'svelte' {
* Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component * Hydrates a component on the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component
* *
* */ * */
export function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>, Events extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, Events, any>>, options: { export function hydrate<Props extends Record<string, any>, Exports extends Record<string, any>>(component: ComponentType<SvelteComponent<Props, any, any>> | Component<Props, Exports, any>, options: {
target: Document | Element | ShadowRoot; target: Document | Element | ShadowRoot;
props?: Props | undefined; props?: Props | undefined;
events?: { [Property in keyof Events]: (e: Events[Property]) => any; } | undefined; events?: Record<string, (e: any) => any> | undefined;
context?: Map<any, any> | undefined; context?: Map<any, any> | undefined;
intro?: boolean | undefined; intro?: boolean | undefined;
recover?: boolean | undefined; recover?: boolean | undefined;
@ -1952,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> & { 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>>; component: import("svelte").ComponentType<import("svelte").SvelteComponent<Props, Events, Slots>> | import("svelte").Component<Props, any, "">;
immutable?: boolean | undefined; immutable?: boolean | undefined;
hydrate?: boolean | undefined; hydrate?: boolean | undefined;
recover?: boolean | undefined; recover?: boolean | undefined;
@ -1963,7 +2001,7 @@ declare module 'svelte/legacy' {
* @deprecated Use this only as a temporary solution to migrate your imperative component code to Svelte 5. * @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").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, "">): 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. * Runs the given function once immediately on the server, and works like `$effect.pre` on the client.
* *

@ -86,6 +86,37 @@ import App from './App.svelte';
`render` also no longer returns CSS; it should be served separately from a CSS file. `render` also no longer returns CSS; it should be served separately from a CSS file.
### Component typing changes
The change from classes towards functions is also reflected in the typings: `SvelteComponent`, the base class from Svelte 4, is deprecated in favor of the new `Component` type which defines the function shape of a Svelte component. To manually define a component shape in a `d.ts` file:
```ts
import type { Component } from 'svelte';
export declare const MyComponent: Component<{
foo: string;
}>;
```
To declare that a component of a certain type is required:
```svelte
<script lang="ts">
import type { Component } from 'svelte';
import {
ComponentA,
ComponentB
} from 'component-library';
let component: Component<{ foo: string }> = $state(
Math.random() ? ComponentA : ComponentB
);
</script>
<svelte:component this={component} foo="bar" />
```
The two utility types `ComponentEvents` and `ComponentType` are also deprecated. `ComponentEvents` is obsolete because events are defined as callback props now, and `ComponentType` is obsolete because the new `Component` type is the component type already (e.g. `ComponentType<SvelteComponent<{ prop: string }>>` == `Component<{ prop: string }>`).
### bind:this changes ### bind:this changes
Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property. Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property.

Loading…
Cancel
Save