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

Form array doesn't work with Typescript #154

Open
mpicciolli opened this issue Jan 10, 2022 · 2 comments
Open

Form array doesn't work with Typescript #154

mpicciolli opened this issue Jan 10, 2022 · 2 comments
Labels
bug Something isn't working

Comments

@mpicciolli
Copy link

mpicciolli commented Jan 10, 2022

Summary

Steps to reproduce

Copy paste the form array code example here and change the script lang for ts

Example Project

<script lang="ts">
    import { createForm } from 'svelte-forms-lib';
    import * as yup from 'yup';

    const { form, errors, state, handleChange, handleSubmit, handleReset } = createForm({
        initialValues: {
            users: [
                {
                    name: '',
                    email: '',
                },
            ],
        },
        validationSchema: yup.object().shape({
            users: yup.array().of(
                yup.object().shape({
                    name: yup.string().required(),
                    email: yup.string().email().required(),
                })
            ),
        }),
        onSubmit: (values) => {
            alert(JSON.stringify(values));
        },
    });

    const add = () => {
        $form.users = $form.users.concat({ name: '', email: '' });
        $errors.users = $errors.users.concat({ name: '', email: '' });
    };

    const remove = (i) => () => {
        $form.users = $form.users.filter((u, j) => j !== i);
        $errors.users = $errors.users.filter((u, j) => j !== i);
    };
</script>

<form>
    <h1>Add users</h1>

    {#each $form.users as user, j}
        <div class="form-group">
            <div>
                <input
                    name={`users[${j}].name`}
                    placeholder="name"
                    on:change={handleChange}
                    on:blur={handleChange}
                    bind:value={$form.users[j].name}
                />
                {#if $errors.users[j].name}
                    <small class="error">{$errors.users[j].name}</small>
                {/if}
            </div>

            <div>
                <input
                    placeholder="email"
                    name={`users[${j}].email`}
                    on:change={handleChange}
                    on:blur={handleChange}
                    bind:value={$form.users[j].email}
                />
                {#if $errors.users[j].email}
                    <small class="error">{$errors.users[j].email}</small>
                {/if}
            </div>

            {#if j === $form.users.length - 1}
                <button type="button" on:click={add}>+</button>
            {/if}
            {#if $form.users.length !== 1}
                <button type="button" on:click={remove(j)}>-</button>
            {/if}
        </div>
    {/each}

    <div class="button-group">
        <button type="button" on:click={handleSubmit}>submit</button>
        <button type="button" on:click={handleReset}>reset</button>
    </div>
</form>

<style>
    .error {
        display: block;
        color: red;
    }
    .form-group {
        display: flex;
        align-items: baseline;
    }
    .button-group {
        display: flex;
    }
    button ~ button {
        margin-left: 15px;
    }
</style>

CodeSandox

What is the current bug behavior?

There are some typescript errors :

  • Argument of type '{ name: string; email: string; }' is not assignable to parameter of type 'string'.
  • Property 'name' does not exist on type 'string'

What is the expected correct behavior?

No typescript error anymore

Relevant logs and/or screenshots

image

image

@mpicciolli mpicciolli added the bug Something isn't working label Jan 10, 2022
@mpicciolli mpicciolli changed the title Form array doesn't work with typescript Form array doesn't work with Typescript Jan 10, 2022
@florimondmanca
Copy link

florimondmanca commented Apr 4, 2022

Same experience on using a yup.array(yup.string()).

Currently my workaround is to explicitly cast:

$: fruitErrors = $errors.fruits as unknown as string[];

Looks like this comes from $errors as being a record whose values are always string:

https://github.com/tjinauyeung/svelte-forms-lib/blob/master/lib/index.d.ts#L34

Should errors be Writable<Inf> directly, so its values are supposed to have the same shape as the form values? Looks like this is what svelte-forms-lib actually exposes. At least when using yup -- this seems to be specific:

if (validationSchema) {
isValidating.set(true);
return (
validationSchema
.validate(values, {abortEarly: false})
.then(() => clearErrorsAndSubmit(values))
// eslint-disable-next-line unicorn/catch-error-name
.catch((yupErrors) => {
if (yupErrors && yupErrors.inner) {
const updatedErrors = getInitial.errors();
yupErrors.inner.map((error) =>
util.set(updatedErrors, error.path, error.message),
);
errors.set(updatedErrors);
}
isSubmitting.set(false);
})
.finally(() => isValidating.set(false))
);
}

I'm not TypeScript definitions guru, but I assume it would be possible to refine createForm and FormState declarations to accept a generic parameter TShape extends yup.ObjectShape which would be plugged into validationSchema: ObjectSchema<TShape> and into errors: Writable<TShape>. Currently validationSchema is marked as optional but it seems to be the condition between a generic form and a yup-powered form.

@ecker00
Copy link

ecker00 commented Jun 25, 2022

Another solution to this is to cast is before assigning it to

.

import type { FormProps } from 'svelte-forms-lib';
import { Form, Field } from 'svelte-forms-lib';

// Specify your own form field type, to you have safe typing
type FormFields = {
  email: string
  username: string
  password: string
}

const formProps: FormProps<FormFields> = {
    initialValues: {}
    .... etc
}

// Cast formProps to the type <Form> expects
const props = formProps as FormProps; // ← ← ←

Then assign props instead of formProps.

<Form {...props}>
    <!-- form contents -->
</Form>

But this behavior does seem like a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants