Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🐛 BUG: Props are not inferred with a Svelte generic component #772

Open
ArmandPhilippot opened this issue Feb 5, 2024 · 4 comments
Open
Labels
- P3: minor bug An edge case that only affects very specific usage (priority) feat: svelte Related to the Svelte integration (scope)

Comments

@ArmandPhilippot
Copy link

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.

import of a Svelte generic component in Astro

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:

  1. 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)
  2. Answer Yes for everything (and keep Strict for Typescript)
  3. 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>
  1. Import this component in another Svelte file src/components/SvelteTest.svelte:
<script>
  import List from "./List.svelte";
</script>

<List />
  1. Import the component in an Astro file src/components/AstroTest.astro:
---
import ListSvelte from "./List.svelte";
---

<ListSvelte />
  1. 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)
@github-actions github-actions bot added the needs triage Issue needs to be triaged label Feb 5, 2024
@Princesseuh
Copy link
Member

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!

@Princesseuh Princesseuh added - P3: minor bug An edge case that only affects very specific usage (priority) feat: svelte Related to the Svelte integration (scope) and removed needs triage Issue needs to be triaged labels Feb 6, 2024
@ArmandPhilippot
Copy link
Author

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 [email protected]. So the problem must lie elsewhere...

@ArmandPhilippot
Copy link
Author

ArmandPhilippot commented Feb 6, 2024

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.

@Princesseuh
Copy link
Member

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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- P3: minor bug An edge case that only affects very specific usage (priority) feat: svelte Related to the Svelte integration (scope)
Projects
None yet
Development

No branches or pull requests

2 participants