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 () => {