chore: better types

pull/18058/head
paoloricciuti 5 months ago
parent 1270d5d434
commit c573720d39

@ -1,4 +1,5 @@
/** @import { ComponentContext } from '#client' */ /** @import { ComponentContext } from '#client' */
/** @import { Component, ComponentType, SvelteComponent } from '../../../index.js' */
import { boundary } from '../dom/blocks/boundary.js'; import { boundary } from '../dom/blocks/boundary.js';
import { branch, effect_root } from '../reactivity/effects.js'; import { branch, effect_root } from '../reactivity/effects.js';
import { push, pop, component_context } from '../context.js'; import { push, pop, component_context } from '../context.js';
@ -38,26 +39,27 @@ import { push_renderer } from './state.js';
* @template {object} [TTextNode=object] * @template {object} [TTextNode=object]
* @template {object} [TComment=object] * @template {object} [TComment=object]
* @param {Renderer<TFragment, TElement, TTextNode, TComment>} renderer * @param {Renderer<TFragment, TElement, TTextNode, TComment>} renderer
* @returns {Renderer<TFragment, TElement, TTextNode, TComment> & { render: (Component: any, options: { target: TFragment | TElement | TTextNode | TComment, props?: any, context?: Map<any, any> }) => () => void }} * @returns {Renderer<TFragment, TElement, TTextNode, TComment> & { render: <Props extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, any, any>, options: {} extends Props ? { target: TFragment | TElement | TTextNode | TComment, props?: Props, context?: Map<any, any> } : { target: TFragment | TElement | TTextNode | TComment, props: Props, context?: Map<any, any> }) => () => void }}
*/ */
export function createRenderer(renderer) { export function createRenderer(renderer) {
return { return {
...renderer, ...renderer,
/** /**
* @param {*} Component * @template {Record<string, any>} Props
* @param {*} options * @param {ComponentType<SvelteComponent<Props>> | Component<Props, any, any>} Component
* @param {{} extends Props ? { target: TFragment | TElement | TTextNode | TComment, props?: Props, context?: Map<any, any> } : { target: TFragment | TElement | TTextNode | TComment, props: Props, context?: Map<any, any> }} options
*/ */
render(Component, { target, props, context }) { render(Component, { target, props, context }) {
var cleanup = push_renderer(renderer); var cleanup = push_renderer(renderer);
const unmount = effect_root(() => { const unmount = effect_root(() => {
var anchor = renderer.createComment(''); var anchor = renderer.createComment('');
renderer.insert(target, anchor, null); renderer.insert(/** @type {*} */ (target), anchor, null);
boundary(/** @type {*} */ (anchor), { pending: () => {} }, (anchor) => { boundary(/** @type {*} */ (anchor), { pending: () => {} }, (anchor) => {
push({}); push({});
var ctx = /** @type {ComponentContext} */ (component_context); var ctx = /** @type {ComponentContext} */ (component_context);
if (context) ctx.c = context; if (context) ctx.c = context;
branch(() => { branch(() => {
Component(anchor, props); /** @type {Function} */ (Component)(anchor, props);
}); });
pop(); pop();
}); });

@ -2569,9 +2569,14 @@ declare module 'svelte/reactivity/window' {
declare module 'svelte/renderer' { declare module 'svelte/renderer' {
export function createRenderer<TFragment extends object = object, TElement extends object = object, TTextNode extends object = object, TComment extends object = object>(renderer: Renderer<TFragment, TElement, TTextNode, TComment>): Renderer<TFragment, TElement, TTextNode, TComment> & { export function createRenderer<TFragment extends object = object, TElement extends object = object, TTextNode extends object = object, TComment extends object = object>(renderer: Renderer<TFragment, TElement, TTextNode, TComment>): Renderer<TFragment, TElement, TTextNode, TComment> & {
render: (Component: any, options: { render: <Props extends Record<string, any>>(component: ComponentType<SvelteComponent<Props>> | Component<Props, any, any>, options: {} extends Props ? {
target: TFragment | TElement | TTextNode | TComment; target: TFragment | TElement | TTextNode | TComment;
props?: any; props?: Props;
context?: Map<any, any>;
} : {
target: TFragment | TElement | TTextNode | TComment;
props: Props;
context?: Map<any, any>;
}) => () => void; }) => () => void;
}; };
export type Renderer<TFragment extends object = object, TElement extends object = object, TTextNode extends object = object, TComment extends object = object, TNode = TFragment | TElement | TTextNode | TComment> = { export type Renderer<TFragment extends object = object, TElement extends object = object, TTextNode extends object = object, TComment extends object = object, TNode = TFragment | TElement | TTextNode | TComment> = {
@ -2652,6 +2657,206 @@ declare module 'svelte/renderer' {
*/ */
removeEventListener: (target: TNode, type: string, handler: any, options?: any) => void; removeEventListener: (target: TNode, type: string, handler: any, options?: any) => void;
}; };
/**
* @deprecated In Svelte 4, components are classes. In Svelte 5, they are functions.
* Use `mount` instead to instantiate components.
* See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes)
* for more info.
*/
interface ComponentConstructorOptions<
Props extends Record<string, any> = Record<string, any>
> {
target: Element | Document | ShadowRoot;
anchor?: Element;
props?: Props;
context?: Map<any, any>;
hydrate?: boolean;
intro?: boolean;
recover?: boolean;
sync?: boolean;
idPrefix?: string;
$$inline?: boolean;
transformError?: (error: unknown) => unknown;
}
/**
* Utility type for ensuring backwards compatibility on a type level that if there's a default slot, add 'children' to the props
*/
type Properties<Props, Slots> = Props &
(Slots extends { default: any }
? // This is unfortunate because it means "accepts no props" turns into "accepts any prop"
// but the alternative is non-fixable type errors because of the way TypeScript index
// signatures work (they will always take precedence and make an impossible-to-satisfy children type).
Props extends Record<string, never>
? any
: { children?: any }
: {});
/**
* This was the base class for Svelte components in Svelte 4. Svelte 5+ components
* are completely different under the hood. For typing, use `Component` instead.
* To instantiate components, use `mount` instead.
* See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more info.
*/
class SvelteComponent<
Props extends Record<string, any> = Record<string, any>,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> {
/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */
static element?: typeof HTMLElement;
[prop: string]: any;
/**
* @deprecated This constructor only exists when using the `asClassComponent` compatibility helper, which
* is a stop-gap solution. Migrate towards using `mount` instead. See
* [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes) for more info.
*/
constructor(options: ComponentConstructorOptions<Properties<Props, Slots>>);
/**
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$prop_def: Props; // Without Properties: unnecessary, causes type bugs
/**
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$events_def: Events;
/**
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$slot_def: Slots;
/**
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$bindings?: string;
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes)
* for more info.
*/
$destroy(): void;
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes)
* for more info.
*/
$on<K extends Extract<keyof Events, string>>(
type: K,
callback: (e: Events[K]) => void
): () => void;
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes)
* for more info.
*/
$set(props: Partial<Props>): void;
}
const brand: unique symbol;
type Brand<B> = { [brand]: B };
type Branded<T, B> = T & Brand<B>;
/**
* Internal implementation details that vary between environments
*/
type ComponentInternals = Branded<{}, 'ComponentInternals'>;
/**
* 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 type { 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'} />
* ```
*/
interface Component<
Props extends Record<string, any> = {},
Exports extends Record<string, any> = {},
Bindings extends keyof Props | '' = string
> {
/**
* @param internal An internal object used by Svelte. Do not use or modify.
* @param props The props passed to the component.
*/
(
this: void,
internals: ComponentInternals,
props: Props
): {
/**
* @deprecated This method only exists when using one of the legacy compatibility helpers, which
* is a stop-gap solution. See [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#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 [migration guide](https://svelte.dev/docs/svelte/v5-migration-guide#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 This type is obsolete when working with the new `Component` type.
*
* @description
* Convenience type to get the type of a Svelte component. Useful for example in combination with
* dynamic components using `<svelte:component>`.
*
* Example:
* ```html
* <script lang="ts">
* import type { ComponentType, SvelteComponent } from 'svelte';
* import Component1 from './Component1.svelte';
* import Component2 from './Component2.svelte';
*
* const component: ComponentType = someLogic() ? Component1 : Component2;
* const componentOfCertainSubType: ComponentType<SvelteComponent<{ needsThisProp: string }>> = someLogic() ? Component1 : Component2;
* </script>
*
* <svelte:component this={component} />
* <svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
* ```
*/
type ComponentType<Comp extends SvelteComponent = SvelteComponent> = (new (
options: ComponentConstructorOptions<
Comp extends SvelteComponent<infer Props> ? Props : Record<string, any>
>
) => Comp) & {
/** The custom element version of the component. Only present if compiled with the `customElement` compiler option */
element?: typeof HTMLElement;
};
export {}; export {};
} }

Loading…
Cancel
Save