breaking: deprecate SvelteComponentTyped, add generics to SvelteComponent (#8512)

Also add data- attribute to HTMLAttributes and use available TS interfaces
pull/8566/head
Simon H 1 year ago committed by GitHub
parent df2f656557
commit 572f5372d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,6 +8,7 @@
* **breaking** Stricter types for `createEventDispatcher` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `Action` and `ActionReturn` (see PR for migration instructions) ([#7224](https://github.com/sveltejs/svelte/pull/7224))
* **breaking** Stricter types for `onMount` - now throws a type error when returning a function asynchronously to catch potential mistakes around callback functions (see PR for migration instructions) ([#8136](https://github.com/sveltejs/svelte/pull/8136))
* **breaking** Deprecate `SvelteComponentTyped`, use `SvelteComponent` instead ([#8512](https://github.com/sveltejs/svelte/pull/8512))
* Add `a11y no-noninteractive-element-interactions` rule ([#8391](https://github.com/sveltejs/svelte/pull/8391))
* Add `a11y-no-static-element-interactions`rule ([#8251](https://github.com/sveltejs/svelte/pull/8251))
* Bind `null` option and input values consistently ([#8312](https://github.com/sveltejs/svelte/issues/8312))

@ -84,9 +84,9 @@ export interface DOMAttributes<T extends EventTarget> {
'on:beforeinput'?: EventHandler<InputEvent, T> | undefined | null;
'on:input'?: FormEventHandler<T> | undefined | null;
'on:reset'?: FormEventHandler<T> | undefined | null;
'on:submit'?: EventHandler<Event & { readonly submitter: HTMLElement | null; }, T> | undefined | null; // TODO make this SubmitEvent once we require TS>=4.4
'on:submit'?: EventHandler<SubmitEvent, T> | undefined | null;
'on:invalid'?: EventHandler<Event, T> | undefined | null;
'on:formdata'?: EventHandler<Event & { readonly formData: FormData; }, T> | undefined | null; // TODO make this FormDataEvent once we require TS>=4.4
'on:formdata'?: EventHandler<FormDataEvent, T> | undefined | null;
// Image Events
'on:load'?: EventHandler | undefined | null;
@ -547,9 +547,9 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D
'bind:innerText'?: string | undefined | null;
readonly 'bind:contentRect'?: DOMRectReadOnly | undefined | null;
readonly 'bind:contentBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4
readonly 'bind:borderBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4
readonly 'bind:devicePixelContentBoxSize'?: Array<{ blockSize: number; inlineSize: number }> | undefined | null; // TODO make this ResizeObserverSize once we require TS>=4.4
readonly 'bind:contentBoxSize'?: Array<ResizeObserverSize> | undefined | null;
readonly 'bind:borderBoxSize'?: Array<ResizeObserverSize> | undefined | null;
readonly 'bind:devicePixelContentBoxSize'?: Array<ResizeObserverSize> | undefined | null;
// SvelteKit
'data-sveltekit-keepfocus'?: true | '' | 'off' | undefined | null;
@ -558,6 +558,9 @@ export interface HTMLAttributes<T extends EventTarget> extends AriaAttributes, D
'data-sveltekit-preload-data'?: true | '' | 'hover' | 'tap' | 'off' | undefined | null;
'data-sveltekit-reload'?: true | '' | 'off' | undefined | null;
'data-sveltekit-replacestate'?: true | '' | 'off' | undefined | null;
// allow any data- attribute
[key: `data-${string}`]: any;
}
export type HTMLAttributeAnchorTarget =

@ -157,10 +157,13 @@ export function construct_svelte_component_dev(component, props) {
}
}
type Props = Record<string, any>;
export interface SvelteComponentDev {
$set(props?: Props): void;
$on(event: string, callback: ((event: any) => void) | null | undefined): () => void;
export interface SvelteComponentDev<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any // eslint-disable-line @typescript-eslint/no-unused-vars
> {
$set(props?: Partial<Props>): void;
$on<K extends Extract<keyof Events, string>>(type: K, callback: ((e: Events[K]) => void) | null | undefined): () => void;
$destroy(): void;
[accessor: string]: any;
}
@ -177,8 +180,33 @@ export interface ComponentConstructorOptions<Props extends Record<string, any> =
/**
* Base class for Svelte components with some minor dev-enhancements. Used when dev=true.
*
* 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'} />
* ```
*/
export class SvelteComponentDev extends SvelteComponent {
export class SvelteComponentDev<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> extends SvelteComponent {
/**
* @private
* For type checking capabilities only.
@ -192,16 +220,16 @@ export class SvelteComponentDev extends SvelteComponent {
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$events_def: any;
$$events_def: Events;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$slot_def: any;
$$slot_def: Slots;
constructor(options: ComponentConstructorOptions) {
constructor(options: ComponentConstructorOptions<Props>) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error("'target' is a required option");
}
@ -221,82 +249,21 @@ export class SvelteComponentDev extends SvelteComponent {
$inject_state() {}
}
// TODO https://github.com/microsoft/TypeScript/issues/41770 is the reason
// why we have to split out SvelteComponentTyped to not break existing usage of SvelteComponent.
// Try to find a better way for Svelte 4.0.
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface SvelteComponentTyped<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any // eslint-disable-line @typescript-eslint/no-unused-vars
> {
$set(props?: Partial<Props>): void;
$on<K extends Extract<keyof Events, string>>(type: K, callback: ((e: Events[K]) => void) | null | undefined): () => void;
$destroy(): void;
[accessor: string]: any;
}
Slots extends Record<string, any> = any
> extends SvelteComponentDev<Props, Events, Slots> {}
/**
* Base class to create strongly typed Svelte components.
* This only exists for typing purposes and should be used in `.d.ts` files.
*
* ### 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 { SvelteComponentTyped } from "svelte";
* export class MyComponent extends SvelteComponentTyped<{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'} />
* ```
*
* #### Why not make this part of `SvelteComponent(Dev)`?
* Because
* ```ts
* class ASubclassOfSvelteComponent extends SvelteComponent<{foo: string}> {}
* const component: typeof SvelteComponent = ASubclassOfSvelteComponent;
* ```
* will throw a type error, so we need to separate the more strictly typed class.
* @deprecated Use `SvelteComponent` instead. See PR for more information: https://github.com/sveltejs/svelte/pull/8512
*/
export class SvelteComponentTyped<
Props extends Record<string, any> = any,
Events extends Record<string, any> = any,
Slots extends Record<string, any> = any
> extends SvelteComponentDev {
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$prop_def: Props;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$events_def: Events;
/**
* @private
* For type checking capabilities only.
* Does not exist at runtime.
* ### DO NOT USE!
*/
$$slot_def: Slots;
constructor(options: ComponentConstructorOptions<Props>) {
super(options);
}
}
> extends SvelteComponentDev<Props, Events, Slots> {}
/**
* Convenience type to get the type of a Svelte component. Useful for example in combination with
@ -305,21 +272,21 @@ export class SvelteComponentTyped<
* Example:
* ```html
* <script lang="ts">
* import type { ComponentType, SvelteComponentTyped } from 'svelte';
* 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<SvelteComponentTyped<{ needsThisProp: string }>> = someLogic() ? Component1 : Component2;
* const componentOfCertainSubType: ComponentType<SvelteComponent<{ needsThisProp: string }>> = someLogic() ? Component1 : Component2;
* </script>
*
* <svelte:component this={component} />
* <svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
* ```
*/
export type ComponentType<Component extends SvelteComponentTyped = SvelteComponentTyped> = new (
export type ComponentType<Component extends SvelteComponentDev = SvelteComponentDev> = new (
options: ComponentConstructorOptions<
Component extends SvelteComponentTyped<infer Props> ? Props : Record<string, any>
Component extends SvelteComponentDev<infer Props> ? Props : Record<string, any>
>
) => Component;
@ -334,7 +301,7 @@ export type ComponentType<Component extends SvelteComponentTyped = SvelteCompone
* </script>
* ```
*/
export type ComponentProps<Component extends SvelteComponent> = Component extends SvelteComponentTyped<infer Props>
export type ComponentProps<Component extends SvelteComponent> = Component extends SvelteComponentDev<infer Props>
? Props
: never;
@ -354,7 +321,7 @@ export type ComponentProps<Component extends SvelteComponent> = Component extend
* ```
*/
export type ComponentEvents<Component extends SvelteComponent> =
Component extends SvelteComponentTyped<any, infer Events> ? Events : never;
Component extends SvelteComponentDev<any, infer Events> ? Events : never;
export function loop_guard(timeout) {
const start = Date.now();

Loading…
Cancel
Save