llulai
llulai

Reputation: 677

dynamic props for svelte component

I have a svelte component that receives two props: value and multiple. multiple has boolean type, and value's type depends on whether multiple is true or false. If I used these props in a function I would do something like:

interface Single {
  multiple: false;
  value: string | null
};

interface Multiple {
  multiple: true;
  value: string[] | [];
};

const doSomenthing = (props: Single | Multiple) => {};

Typescript would complain if I try to call the function with:

doSomething({ multiple: false, value: ['hello', 'world'] })

So far, I haven't figured the way of doing the same thing on svelte, so that if I instantiate the component:

<MyComponent multiple={false} value={['hello', 'world']} />

throws an error.

Upvotes: 0

Views: 1190

Answers (3)

brunnerh
brunnerh

Reputation: 185225

In this case you can use one generic parameter for multiple to make value dependent on it:

<script lang="ts" generics="T extends boolean">
    export let multiple: T;
    export let value: T extends true ? string[] : string;
</script>

(Note that generics syntax is not finalized. RFC - Feedback thread)

Unfortunately {#if multiple} will currently not narrow the type of value correctly. So maybe it would be better to just drop multiple altogether and just use Array.isArray(value) in its place.

<script lang="ts">
    export let value: string[] | string;
</script>

{#if Array.isArray(value)}
    {value.join(', ')}
{:else}
    {value}
{/if}

Upvotes: 0

Corrl
Corrl

Reputation: 7721

I see you're asking for setting the props seperately

<MyComponent multiple={false} value={['hello', 'world']} />

but why not export one prop and use ComponentProps to check for the correct type - REPL

Comp.svelte
<script lang="ts">
    
    interface Single {
        multiple: false;
        value: string | null;
    }

    interface Multiple {
        multiple: true;
        value: string[] | [];
    }

    export let singleOrMultiple: Single | Multiple;
    
</script>

Comp - {singleOrMultiple.multiple} {singleOrMultiple.value}
<script lang="ts">
    import Comp from '$lib/Comp.svelte';
    import type { ComponentProps } from 'svelte';

    const props: ComponentProps<Comp> = {
        singleOrMultiple: { multiple: false, value: null }
    };
    
</script>

<Comp {...props} />

Upvotes: 0

Stephane Vanraes
Stephane Vanraes

Reputation: 16451

The only thing I can come up with is to type the $$Props generally, note that you would still have to type the exports individually with all possibilities as well. Meaning this is not perfect either because internally the type of value can be all four.

<script lang="ts">
    type Multiple = { multiple: true; value: string[] | [] };
    type Single = { multiple: false; value: string | null };
    type $$Props = Multiple | Single;

    export let multiple: boolean;
    export let value: string | string[] | [] | null;
</script>

{#if multiple}
    {#each value as item}a{/each}
{:else}
    {#each value as item}a{/each}
{/if}

the above code will give errors when using the component wrong as you requested, but it will also give errors on the #each block because null is not an iterable.

Upvotes: 0

Related Questions