From 6f575715073f4a1eb1abdd7a2d22a75ae6017cf7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 1 Jul 2022 14:48:10 +0200 Subject: [PATCH] [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 Co-authored-by: Hofer Ivan Co-authored-by: Ignatius Bagus --- CHANGELOG.md | 1 + generate-type-definitions.js | 13 ++++++++++ package-lock.json | 12 +++++----- package.json | 2 +- src/runtime/index.ts | 1 + src/runtime/internal/dev.ts | 46 +++++++++++++++++++++++++++++++++--- 6 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 generate-type-definitions.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ca35627cf3..b5bc4858d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Faster SSR ([#5701](https://github.com/sveltejs/svelte/pull/5701)) * Fix `class:` directive updates with `` ([#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 diff --git a/generate-type-definitions.js b/generate-type-definitions.js new file mode 100644 index 00000000000..6cad31807f0 --- /dev/null +++ b/generate-type-definitions.js @@ -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')); diff --git a/package-lock.json b/package-lock.json index 3e84c9b8c6f..83d963b827e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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": { diff --git a/package.json b/package.json index b3c9c65f986..a1c9ae9a5d9 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/runtime/index.ts b/src/runtime/index.ts index 8e12f9f0eeb..08b25ba2ca4 100644 --- a/src/runtime/index.ts +++ b/src/runtime/index.ts @@ -13,4 +13,5 @@ export { createEventDispatcher, SvelteComponentDev as SvelteComponent, SvelteComponentTyped + // additional exports added through post-typegen.js } from 'svelte/internal'; diff --git a/src/runtime/internal/dev.ts b/src/runtime/internal/dev.ts index 3144911672e..5b00e7dc5b4 100644 --- a/src/runtime/internal/dev.ts +++ b/src/runtime/internal/dev.ts @@ -128,7 +128,7 @@ export interface SvelteComponentDev { $destroy(): void; [accessor: string]: any; } -interface IComponentOptions = Record> { +export interface ComponentConstructorOptions = Record> { 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) { + constructor(options: ComponentConstructorOptions) { super(options); } } +/** + * Convenience type to get the type of a Svelte component. Useful for example in combination with + * dynamic components using ``. + * + * Example: + * ```html + * + * + * + * + * ``` + */ +export type ComponentType = new ( + options: ComponentConstructorOptions< + Component extends SvelteComponentTyped ? Props : Record + > +) => Component; + +/** + * Convenience type to get the props the given component expects. Example: + * ```html + * + * ``` + */ +export type ComponentProps = Component extends SvelteComponentTyped + ? Props + : never; + export function loop_guard(timeout) { const start = Date.now(); return () => {