Svelte Components as Object Properties

Viewed 2420

I want to accept a component as a prop.

import AComponent from './AComponent.svelte';

type MyType = {
  [key: string]: any; // just an example
}

type IWantToAcceptAComponent<T> = {
  component: SvelteComponentTyped<{ record: T }>;
}

const componentPropObject: IWantToAcceptAComponent<MyType> = {
  component: AComponent // error here
}
Error: Type 'typeof AComponent__SvelteComponent_' is missing the following properties from type 
'SvelteComponentTyped<{ record: MyType; }>': $set, $on, $destroy, $$prop_def, and 5 
more.ts(2740)

I figured it might be that the typedef should be:

type IWantToAcceptAComponent<T> = {
  component: typeof SvelteComponentTyped<{ record: T }>;
}

But that causes even weirder errors.

It's fairly important that this typing is explicit, as it's supposed to be part of an exported interface for users.

2 Answers

You need to adjust your typing of IWantToAcceptAComponent. You can't use SvelteComponentTyped directly because that means you want the instance type, not the constructor type. But you also can't do typeof SvelteComponentType<..some generic> because that's a syntax error for TypeScript. You need to type the "accepts a class constructor that returns a specific instance" like this:

type IWantToAcceptAComponent<T> = {
  component: new (...args: any) => SvelteComponentTyped<{ record: T }>;
}

Note that you need Svelte for VS Code version 105 or greater and (if you use it) svelte-check 2.0.0 or greater for this to work. There was a slight mismatch of the typings previously.

If you want to use the code inside a TypeScript file, you need to activate the TypeScript plugin of the Svelte for VS Code extension. Else all Svelte imports are typed as a generic SvelteComponentDev without the proper types.

Even though I don't use typescript, I see no reasons why this would be possible with pure JS but not with TS. I've made REPL which basically wraps two other 'sub components' passed as props.

The structure is something like :

App.svelte

<script>
  //App.svelte
  import A from "./A.svelte";
  import B from "./B.svelte";
  import Wrapper from "./Wrapper.svelte";

  let props = [  { component: A }, { component: B } ];
</script>

<Wrapper {props} />

Wrapper.svelte :

<script>
    //Wrapper.svelte
    export let props = [];
</script>

{#each props as prop}
    <p>
        <svelte:component this={prop.component} />
    </p>
{/each}

A.svelte :

   <!-- A.svelte -->
   <h2>COMP A</h2>

B.svelte :

    <!-- B.svelte -->    
    <h3>COMP B</h3>
Related