๐ BUG: Props are not inferred with a Svelte generic component
Opened this issue ยท 4 comments
Describe the Bug
Before all, once again, I'm note sure if it is a compiler or a language-tools issue. I guess it is not the compiler since the app can be build without errors. Sorry if I picked up the wrong repository.
Context
Svelte supports the creation of generic components using Typescript. The syntax that was retained looks like:
<script lang="ts" generics="T extends boolean, X">
import {createEventDispatcher} from "svelte";
export let array1: T[];
export let item1: T;
export let array2: X[];
const dispatch = createEventDispatcher<{arrayItemClick: X}>();
</script>
However, it seems this API is still experimental and not yet documented on the Svelte website.
The issue
When importing a generic component in a Svelte file, everything works as expected: the props are correctly inferred and we can have auto-completion in VS Code.
When importing the same generic component in an Astro file, the app can be build (no errors, and the component is visible in the browser). However, the Props are not inferred and Typescript complains.
Example
With this component:
<script lang="ts" generics="T extends boolean = false">
import type { SvelteHTMLElements } from "svelte/elements";
type BaseProps = T extends true
? SvelteHTMLElements["ol"]
: SvelteHTMLElements["ul"];
type $$Props = BaseProps & {
isOrdered?: T;
};
export let isOrdered = false;
let tag: Extract<keyof HTMLElementTagNameMap, "ol" | "ul">;
$: tag = isOrdered ? "ol" : "ul";
</script>
<svelte:element this={tag} {...$$restProps}>
<slot />
</svelte:element>
If I try to import it in a file with the .astro
extension, I can't benefit from the intellisense.
And Typescript gives me the following error:
Type '{}' is not assignable to type 'IntrinsicAttributes & ComponentConstructorOptions<$$Props>'.
Property 'target' is missing in type '{}' but required in type 'ComponentConstructorOptions<$$Props>'.ts(2322)
My expectations
No errors from Typescript and the ability to use the intellisense to fill the props, like any component.
System info
> astro info
Astro v4.3.2
Node v18.17.1
System Linux (x64)
Package Manager npm
Output static
Adapter none
Integrations @astrojs/svelte
which: no xclip in .......
Steps to Reproduce
I created a Stackblitz that can be downloaded and opened in VS Code (since the parser won't work otherwise). It contains three files in src/components
:
- a generic Svelte component (
List.svelte
) - a Svelte file to test the import (
SvelteTest.svelte
) - an Astro file to test the import (
AstroTest.svelte
)
Here the steps to manually reproduce the error:
- In VS Code, create a new Astro project with
npm create astro@latest -- --template framework-svelte
(and install the Astro and Svelte extensions if needed) - Answer
Yes
for everything (and keepStrict
for Typescript) - Create a file
src/components/List.svelte
that contains our generic component:
<script lang="ts" generics="T extends boolean = false">
import type { SvelteHTMLElements } from "svelte/elements";
type BaseProps = T extends true
? SvelteHTMLElements["ol"]
: SvelteHTMLElements["ul"];
type $$Props = BaseProps & {
isOrdered?: T;
};
export let isOrdered = false;
let tag: Extract<keyof HTMLElementTagNameMap, "ol" | "ul">;
$: tag = isOrdered ? "ol" : "ul";
</script>
<svelte:element this={tag} {...$$restProps}>
<slot />
</svelte:element>
- Import this component in another Svelte file
src/components/SvelteTest.svelte
:
<script>
import List from "./List.svelte";
</script>
<List />
- Import the component in an Astro file
src/components/AstroTest.astro
:
---
import ListSvelte from "./List.svelte";
---
<ListSvelte />
- Now if we look our two tests files:
- the Svelte one correctly infer the Props:
class List<T extends boolean = false>
- the Astro one does not infer the Props and Typescript is complaining with:
Type '{}' is not assignable to type 'IntrinsicAttributes & ComponentConstructorOptions<$$Props>'.
Property 'target' is missing in type '{}' but required in type 'ComponentConstructorOptions<$$Props>'.ts(2322)
Might just be a question of updating the svelte2tsx
dependency of @astrojs/svelte
, if this is a new feature.
Thank you for submitting an issue!
I just looked in the svelte2tsx
releases. It seems it was added in svelte2tsx-0.6.15
(which was released in May 26, 2023). The @astrojs/svelte
package already uses "svelte2tsx": "^0.6.25"
. I checked the example I set up yesterday and the installed version is svelte2tsx@0.6.27
. So the problem must lie elsewhere...
I dig up a little to understand how the integration is done. I have not tested but I think the issue comes from these lines in @astrojs/svelte
.
When a component does not use the generics
attribute, it seems the output matches:
export default class extends __sveltets_2_createSvelte2TsxComponent(
We can see that this is the case in the following Svelte tests: component-default-slot
and component-with-documentation
.
However, when the generics
attribute is used, the output seems different according to these other tests generic-attribute-const-modifier
and ts-script-tag-generics
(or ts-$$generics
for the discarded syntax):
export default class Input__SvelteComponent_<const T extends readonly string[]> extends __SvelteComponentTyped__<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> {
So I guess we are loosing the correct types because of the tsx.replace
which is no longer accurate.
If I'm correct it seems I pick up the wrong repository again. The issue belongs to the astro repository
.
Thank you for investigating! That replace is definitely brittle, I kinda wish we could remove it. Maybe in the future.
Despite this issue being in code that's in the main repo, we typically keep issues about this here, so no worries!