How do I create a custom input svelte component and reuse prop types?

Viewed 937

I'm trying to create a wrapped version of an input component with a label, I want it to have all the same attributes as the normal input field, as well as the "label".

I'm slightly confused about how to import the type definition and to type the attributes properly.

<style>
    input {
        width: 100%;
        display: block;
    }
</style>

<script lang="typescript">
    import { SvelteInputProps } from 'svelte'
    export let label: string = '' 
    type $$Props = SvelteInputProps;
</script>

<label>
    {label}
    <input {...$$props} />
</label>

like this in react:

interface Props extends React.InputHTMLAttributes<HTMLInputElement> {
    label: string
}

export default function Input (props: Props) {
    const { label, ...inputProps } = props;
    return (
        <label>
            {label}
            <input {...inputProps}></input>
        </label>
    )
}
4 Answers
  1. I suggest changing $$props to $$restProps. $$restProps includes all the props not explicitly exported, which means the label prop is excluded, which is likely what you want.
  2. I suggest changing lang="typescript" to lang="ts", which will become the only valid way of signaling that the script code is TypeScript in the long run.
  3. To express "extend from all valid input props", write interface $$Props extends .. { label: string } where .. is something you need to define yourself for now. You can take the contents of the HTMLAttributes definition as a start (ignoring the interfaces it extends from) and remove all properties that are not valid on input, or accept that this includes more than possible.
<style>
    input {
        width: 100%;
        display: block;
    }
</style>

<script lang="ts">
    import type { SvelteInputProps } from './filepath-to-your-typings'
    export let label: string = '' 
    interface $$Props extends SvelteInputProps {
        label: string;
    }
</script>

<label>
    {label}
    <input {...$$restProps} />
</label>

If you use svelte for vscode, there's a namespace already declared in the global scope (pretty much like the HTML typings provided by vscode), which is svelte.JSX.SvelteInputProps, I think that's what you're looking for. If you don't like the idea of rely on in-editor types (which is pretty reasonable) you can install the types directly in your project. If you wan't the very same interface, this is the project, but there are alternatives on definetely typed or go for js-dom types.

Svelte it's a JS framework (Svelte language honestly), and TS working on top. It means, we have no types across component borders. You can check the RFC proposal https://github.com/sveltejs/rfcs/pull/38 Personally, I don't like to use TS inside components, in most cases, it's useless and extends compilation time but at the same time, any libs or project "js" files really good to have in TS.

I created my propre input like this:

<script lang="ts"> //here I use ts

  export enum TextType { text = "text", email = "email", password = "password" } // svelte creats differently input when it is type text and differently when type is a number. Probably it is due to propreties "max" and "min" 

  //here are exported props 
  export let type: TextType;
  export let label: string | null = null;
  export let placeholder: string | null = null;
  export let value: string | null = null;

  function typeAction(node: HTMLInputElement) {
    node.type = type
  }//this function helps to define type of input 

</script>

<div class="myInput">
    <label class="myLabel">{label}
      <input on:input bind:value type="text" placeholder={ placeholder }   
             use:typeAction/>
    </label>
</div>

You can create analog input for numbers. You change the type to the number and you add min and max props.

Related