0
votes

I'm working on a refacto for a project written in typescript and using React Hooks. I have some knowledge about typescript though I'm not really an expert, more like beginner.

I'm trying to develop some code reuse for this project (this why the refacto with hooks) and I'm getting stuck about a simple error, I can find a trick of course but I want to discover why it's not working.

I have a simple form for login with 2 inputs (email and password):

LoginForm

import React from 'react';
import { SmartInput, RoundButton } from '@/components';
import { useMultipleInputs } from '@/hooks';

interface ILoginFormProps {
    onLogin: (email: string, password: string) => Promise<void>;
    errorMsg?: string;
}

interface ILoginFormInputs {
    password: string;
    email: string;
}

export const LoginForm = ({
    onLogin,
    errorMsg,
}: ILoginFormProps): JSX.Element => {
    const [inputs, setInputs] = useMultipleInputs<ILoginFormInputs>(); // line where the error occur

    const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
        const { email, password } = inputs;
        e.preventDefault();
        await onLogin(email, password);
    };

    const displayErrorMsg = () => {
        if (errorMsg) {
            return (
                <p className='body-text body-text--medium body-text--error auth-form__form__error-msg'>
                    {errorMsg}
                </p>
            );
        }
        return null;
    };

    return (
        <div className='auth-form'>
            <div className='auth-form__container'>
                <div className='auth-form__container__title title title--big'>
                    Sign in to <br />
                    Esport-Hatcher
                </div>
                <form className='auth-form__basic' onSubmit={onSubmit}>
                    <SmartInput
                        type='email'
                        placeholder='Email'
                        name='email'
                        icon='mail'
                        value={inputs.email}
                        onChange={setInputs}
                    />
                    <SmartInput
                        type='password'
                        placeholder='Password'
                        name='password'
                        icon='lock'
                        value={inputs.password}
                        onChange={setInputs}
                    />
                    {displayErrorMsg()}
                    <RoundButton onClick={() => null} />
                </form>
            </div>
        </div>
    );
};

I want to delay the state of my inputs to a custom hook, so it's reusable.

useMultipleInputs

import React, { useState } from 'react';

interface IInputs {
    [key: string]: string;
}

export const useMultipleInputs = <T extends IInputs>(): [
    T,
    typeof onChange
] => {
    const [inputs, setInputs] = useState<T | {}>({});

    const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setInputs({
            ...inputs,
            [e.target.name]: e.target.value,
        });
    };
    return [inputs as T, onChange];
};

Sadly, as you can see on the first screenshot I get an error:

Type 'ILoginFormInputs' does not satisfy the constraint 'IInputs'.

Index signature is missing in type 'ILoginFormInputs'.

Does typescript not treat { email: string, password: string } as [key: string]: string] ?

Thanks a lot for reading me :)

1
Please consider providing a minimal reproducible example as described in the guidelines for how to ask a good question. Specifically, the relevant code should be included in the question itself (and not only as an external link), and it should be presented as text (suitable for copying/pasting into an IDE) and not as an image. Good luck!jcalz
Ok I'm editing right now !Arthur Gamblin

1 Answers

0
votes

I believe this is by design. You can have multiple interface declarations with the same name and they will merge - but you cannot do that to types. (T is a type not an interface). So TS is just trying to be very safe here.

You could try to change your ILoginFormInputs definition to a type which is stricter.

type ILoginFormInputs = {
    password: string;
    email: string;
}