[feat] add convenience types ComponentType and ComponentProps (#6770)

ComponentType eases typing "this is a variable that expects a Svelte component constructor (of a certain shape)". Removes the need for SvelteComponentTyped to be an extra type so it can be deprecated in v4 and removed in v5, and SvelteComponent(Dev) can receive the same generic typings as SvelteComponetTyped in v4.

ComponentProps eases typing "give me the props this component expects". Closes #7584


Co-authored-by: Simon Holthausen <simon.holthausen@accso.de>
Co-authored-by: Hofer Ivan <ivan.hofer@outlook.com>
Co-authored-by: Ignatius Bagus <ignatius.mbs@gmail.com>
pull/7662/head
Simon H 2 years ago committed by GitHub
parent 2f562d9e28
commit 6f57571507
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -5,6 +5,7 @@
* Faster SSR ([#5701](https://github.com/sveltejs/svelte/pull/5701))
* Fix `class:` directive updates with `<svelte:element>` ([#7521](https://github.com/sveltejs/svelte/issues/7521), [#7571](https://github.com/sveltejs/svelte/issues/7571))
* Harden attribute escaping during ssr ([#7530](https://github.com/sveltejs/svelte/pull/7530))
* Add `ComponentType` and `ComponentProps` convenience types ([#6770](https://github.com/sveltejs/svelte/pull/6770))
## 3.48.0

@ -0,0 +1,13 @@
// This script generates the TypeScript definitions
const { execSync } = require('child_process');
const { readFileSync, writeFileSync } = require('fs');
execSync('tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly');
// We need to add these types to the index.d.ts here because if we add them before building, the build will fail,
// because the TS->JS transformation doesn't know these exports are types and produces code that fails at runtime.
// We can't use `export type` syntax either because the TS version we're on doesn't have this feature yet.
const path = 'types/runtime/index.d.ts';
const content = readFileSync(path, 'utf8');
writeFileSync(path, content.replace('SvelteComponentTyped', 'SvelteComponentTyped, ComponentType, ComponentConstructorOptions, ComponentProps'));

12
package-lock.json generated

@ -4402,9 +4402,9 @@
"dev": true
},
"node_modules/typescript": {
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
@ -8096,9 +8096,9 @@
"dev": true
},
"typescript": {
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"version": "3.9.10",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz",
"integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==",
"dev": true
},
"unbox-primitive": {

@ -95,7 +95,7 @@
"pretest": "npm run build",
"posttest": "agadoo internal/index.mjs",
"prepublishOnly": "node check_publish_env.js && npm run lint && npm test",
"tsd": "tsc -p src/compiler --emitDeclarationOnly && tsc -p src/runtime --emitDeclarationOnly",
"tsd": "node ./generate-type-definitions.js",
"lint": "eslint \"{src,test}/**/*.{ts,js}\""
},
"repository": {

@ -13,4 +13,5 @@ export {
createEventDispatcher,
SvelteComponentDev as SvelteComponent,
SvelteComponentTyped
// additional exports added through post-typegen.js
} from 'svelte/internal';

@ -128,7 +128,7 @@ export interface SvelteComponentDev {
$destroy(): void;
[accessor: string]: any;
}
interface IComponentOptions<Props extends Record<string, any> = Record<string, any>> {
export interface ComponentConstructorOptions<Props extends Record<string, any> = Record<string, any>> {
target: Element | ShadowRoot;
anchor?: Element;
props?: Props;
@ -164,7 +164,7 @@ export class SvelteComponentDev extends SvelteComponent {
*/
$$slot_def: any;
constructor(options: IComponentOptions) {
constructor(options: ComponentConstructorOptions) {
if (!options || (!options.target && !options.$$inline)) {
throw new Error("'target' is a required option");
}
@ -256,11 +256,51 @@ export class SvelteComponentTyped<
*/
$$slot_def: Slots;
constructor(options: IComponentOptions<Props>) {
constructor(options: ComponentConstructorOptions<Props>) {
super(options);
}
}
/**
* 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, SvelteComponentTyped } 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;
* </script>
*
* <svelte:component this={component} />
* <svelte:component this={componentOfCertainSubType} needsThisProp="hello" />
* ```
*/
export type ComponentType<Component extends SvelteComponentTyped = SvelteComponentTyped> = new (
options: ComponentConstructorOptions<
Component extends SvelteComponentTyped<infer Props> ? Props : Record<string, any>
>
) => Component;
/**
* Convenience type to get the props the given component expects. Example:
* ```html
* <script lang="ts">
* import type { ComponentProps } from 'svelte';
* import Component from './Component.svelte';
*
* const props: ComponentProps<Component> = { foo: 'bar' }; // Errors if these aren't the correct props
* </script>
* ```
*/
export type ComponentProps<Component extends SvelteComponent> = Component extends SvelteComponentTyped<infer Props>
? Props
: never;
export function loop_guard(timeout) {
const start = Date.now();
return () => {

Loading…
Cancel
Save