From 39f3d043fc96f228fd8cda972faf78dbb86afa59 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Thu, 31 Oct 2024 17:56:27 +0100 Subject: [PATCH] feat: provide component instance type in Svelte 5 (#2553) While it's a gotcha for people declaring their own types, the vast majority of people will use component types via importing other components, and as such it makes sense to provide the same convenience we know from class components #2522 --- .../src/svelte2tsx/addComponentExport.ts | 23 +++++++++++-------- .../expected/TestRunes.svelte.d.ts | 1 + .../expected/TestRunes.svelte.d.ts | 1 + .../export-list-runes.v5/expectedv2.ts | 1 + .../renamed-exports-runes.v5/expectedv2.ts | 1 + .../runes-best-effort-types.v5/expectedv2.ts | 1 + .../samples/runes-bindable.v5/expectedv2.ts | 1 + .../expectedv2.ts | 1 + .../runes-only-export.v5/expectedv2.ts | 1 + .../svelte2tsx/samples/runes.v5/expectedv2.ts | 1 + .../expectedv2.ts | 1 + .../expectedv2.ts | 1 + .../ts-export-list-runes.v5/expectedv2.ts | 1 + .../expectedv2.ts | 1 + .../ts-runes-bindable.v5/expectedv2.ts | 1 + .../samples/ts-runes.v5/expectedv2.ts | 1 + .../expectedv2.ts | 1 + .../expectedv2.ts | 1 + 18 files changed, 30 insertions(+), 10 deletions(-) diff --git a/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts b/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts index fc9550b77..5df9a5281 100644 --- a/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts +++ b/packages/svelte2tsx/src/svelte2tsx/addComponentExport.ts @@ -174,13 +174,15 @@ function addSimpleComponentExport({ const doc = componentDocumentation.getFormatted(); const className = fileName && classNameFromFilename(fileName, mode !== 'dts'); + const componentName = className || '$$Component'; let statement: string; if (mode === 'dts') { if (isSvelte5 && exportedNames.usesRunes() && !usesSlots && !events.hasEvents()) { statement = - `\n${doc}const ${className || '$$Component'} = __sveltets_2_fn_component(render());\n` + - `export default ${className || '$$Component'};`; + `\n${doc}const ${componentName} = __sveltets_2_fn_component(render());\n` + + `type ${componentName} = ReturnType;\n` + + `export default ${componentName};`; } else if (isSvelte5) { // Inline definitions from Svelte shims; else dts files will reference the globals which will be unresolved statement = @@ -203,11 +205,11 @@ function addSimpleComponentExport({ declare function $$__sveltets_2_isomorphic_component< Props extends Record, Events extends Record, Slots extends Record, Exports extends Record, Bindings extends string >(klass: {props: Props, events: Events, slots: Slots, exports?: Exports, bindings?: Bindings }): $$__sveltets_2_IsomorphicComponent;\n`) + - `${doc}const ${className || '$$Component'} = $$__sveltets_2_isomorphic_component${usesSlots ? '_slots' : ''}(${propDef});\n` + + `${doc}const ${componentName} = $$__sveltets_2_isomorphic_component${usesSlots ? '_slots' : ''}(${propDef});\n` + surroundWithIgnoreComments( - `type ${className || '$$Component'} = InstanceType;\n` + `type ${componentName} = InstanceType;\n` ) + - `export default ${className || '$$Component'};`; + `export default ${componentName};`; } else if (isTsFile) { const svelteComponentClass = noSvelteComponentTyped ? 'SvelteComponent' @@ -244,15 +246,16 @@ declare function $$__sveltets_2_isomorphic_component< if (isSvelte5) { if (exportedNames.usesRunes() && !usesSlots && !events.hasEvents()) { statement = - `\n${doc}const ${className || '$$Component'} = __sveltets_2_fn_component(render());\n` + - `export default ${className || '$$Component'};`; + `\n${doc}const ${componentName} = __sveltets_2_fn_component(render());\n` + + `type ${componentName} = ReturnType;\n` + + `export default ${componentName};`; } else { statement = - `\n${doc}const ${className || '$$Component'} = __sveltets_2_isomorphic_component${usesSlots ? '_slots' : ''}(${propDef});\n` + + `\n${doc}const ${componentName} = __sveltets_2_isomorphic_component${usesSlots ? '_slots' : ''}(${propDef});\n` + surroundWithIgnoreComments( - `type ${className || '$$Component'} = InstanceType;\n` + `type ${componentName} = InstanceType;\n` ) + - `export default ${className || '$$Component'};`; + `export default ${componentName};`; } } else { statement = diff --git a/packages/svelte2tsx/test/emitDts/samples/javascript-runes.v5/expected/TestRunes.svelte.d.ts b/packages/svelte2tsx/test/emitDts/samples/javascript-runes.v5/expected/TestRunes.svelte.d.ts index 04808233f..350d1d487 100644 --- a/packages/svelte2tsx/test/emitDts/samples/javascript-runes.v5/expected/TestRunes.svelte.d.ts +++ b/packages/svelte2tsx/test/emitDts/samples/javascript-runes.v5/expected/TestRunes.svelte.d.ts @@ -4,4 +4,5 @@ declare const TestRunes: import("svelte").Component<{ }, { baz: () => void; }, "bar">; +type TestRunes = ReturnType; export default TestRunes; diff --git a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts index 04808233f..350d1d487 100644 --- a/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts +++ b/packages/svelte2tsx/test/emitDts/samples/typescript-runes.v5/expected/TestRunes.svelte.d.ts @@ -4,4 +4,5 @@ declare const TestRunes: import("svelte").Component<{ }, { baz: () => void; }, "bar">; +type TestRunes = ReturnType; export default TestRunes; diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts index e99e80294..9687d7980 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/export-list-runes.v5/expectedv2.ts @@ -22,4 +22,5 @@ async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} }; return { props: /** @type {Record} */ ({}), exports: /** @type {{name1: typeof name1,name2: typeof name2,renamed1: typeof rename1,renamed2: typeof rename2,Foo: typeof Foo,bar: typeof bar,baz: typeof baz,RenamedFoo: typeof RenameFoo,renamedbar: typeof renamebar,renamedbaz: typeof renamebaz}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts index 8461d6616..5208d8a9b 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/renamed-exports-runes.v5/expectedv2.ts @@ -10,4 +10,5 @@ async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} }; return { props: /** @type {Record} */ ({}), exports: /** @type {{name3: typeof name,name4: typeof name2}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types.v5/expectedv2.ts index b5c446abe..496a607df 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-best-effort-types.v5/expectedv2.ts @@ -6,4 +6,5 @@ async () => {}; return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable.v5/expectedv2.ts index 5c4ac5f23..5379c6643 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-bindable.v5/expectedv2.ts @@ -6,4 +6,5 @@ async () => {}; return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings('b'), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores.v5/expectedv2.ts index 8263d1173..2abbbff95 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-looking-like-stores.v5/expectedv2.ts @@ -10,4 +10,5 @@ async () => { state; derived;}; return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes-only-export.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes-only-export.v5/expectedv2.ts index 0a77ec5d6..854ee6a94 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/runes-only-export.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes-only-export.v5/expectedv2.ts @@ -9,4 +9,5 @@ async () => { x;}; return { props: /** @type {Record} */ ({}), exports: /** @type {{foo: typeof foo}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/runes.v5/expectedv2.ts index 328328888..06698e763 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/runes.v5/expectedv2.ts @@ -9,4 +9,5 @@ async () => {}; return { props: /** @type {$$ComponentProps} */({}), exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes.v5/expectedv2.ts index 6eceb26ee..5e21dd042 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune-no-changes.v5/expectedv2.ts @@ -9,4 +9,5 @@ async () => {}; return { props: /** @type {$$ComponentProps} */({}), exports: /** @type {{snapshot: typeof snapshot}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Page__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Page__SvelteComponent_ = ReturnType; export default Page__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune.v5/expectedv2.ts index eeb3e8864..b88689f8f 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/sveltekit-autotypes-$props-rune.v5/expectedv2.ts @@ -7,4 +7,5 @@ async () => {}; return { props: /** @type {$$ComponentProps} */({}), exports: /** @type {{snapshot: typeof snapshot}} */ ({}), bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Page__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Page__SvelteComponent_ = ReturnType; export default Page__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts index 45c93ecf4..1a1d1cf83 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-export-list-runes.v5/expectedv2.ts @@ -22,4 +22,5 @@ async () => { { svelteHTML.createElement("svelte:options", {"runes":true,});} }; return { props: {} as Record, exports: {} as any as { name1: string,name2: string,name3: string,name4: string,renamed1: string,renamed2: string,Foo: typeof Foo,bar: typeof bar,baz: string,RenamedFoo: typeof RenameFoo,renamedbar: typeof renamebar,renamedbaz: string }, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types.v5/expectedv2.ts index faa1d533b..4ed0b40e0 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-best-effort-types.v5/expectedv2.ts @@ -6,4 +6,5 @@ async () => {}; return { props: {} as any as $$ComponentProps, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable.v5/expectedv2.ts index ce3a575fe..f2686212b 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes-bindable.v5/expectedv2.ts @@ -6,4 +6,5 @@ async () => {}; return { props: {} as any as $$ComponentProps, exports: {}, bindings: __sveltets_$$bindings('b', 'c'), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts index 8e4065235..e248ca3cf 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-runes.v5/expectedv2.ts @@ -8,4 +8,5 @@ async () => {}; return { props: {} as any as $$ComponentProps, exports: {}, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Input__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Input__SvelteComponent_ = ReturnType; export default Input__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts index df7501a9a..04b6fd6ba 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune-unchanged.v5/expectedv2.ts @@ -7,4 +7,5 @@ async () => {}; return { props: {} as any as $$ComponentProps, exports: {} as any as { snapshot: any }, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Page__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Page__SvelteComponent_ = ReturnType; export default Page__SvelteComponent_; \ No newline at end of file diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune.v5/expectedv2.ts b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune.v5/expectedv2.ts index ae77eca3f..9bc14ba91 100644 --- a/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune.v5/expectedv2.ts +++ b/packages/svelte2tsx/test/svelte2tsx/samples/ts-sveltekit-autotypes-$props-rune.v5/expectedv2.ts @@ -7,4 +7,5 @@ async () => {}; return { props: {} as any as $$ComponentProps, exports: {} as any as { snapshot: typeof snapshot }, bindings: __sveltets_$$bindings(''), slots: {}, events: {} }} const Page__SvelteComponent_ = __sveltets_2_fn_component(render()); +type Page__SvelteComponent_ = ReturnType; export default Page__SvelteComponent_; \ No newline at end of file