Salvador Bacci
Salvador Bacci

Reputation: 79

React Typescript - Can't stablish type of object well

i'm just started learning Typescript. I trying to migrate a project from Javascript, but I got stuck with a custom hook.

The hook, useForm, just handle the onChange event of inputs in a form and then updates their values so I can write in and control their states.

interface formProps{
  initialValues: {[key: string]: string}
}

export const useForm = ( initialState: formProps['initialValues'] ) => {
  const [values, setValues] = useState(initialState);

  const reset = () => {
    setValues(initialState);
  }

  const handleInputChange = ({ target }: React.ChangeEvent<HTMLInputElement>) =>{
    setValues({
      ...values,
      [target.name]:target.value
    })
  }
  return [values, handleInputChange, reset]
}

The argument for useForm should be an object with different entries, but the problem is it could be different any time, so I can't specify this.

When I try to use the hook in a component with for example values.userID or values['password`] I get the error:

The property 'password' does not exist on type '{ [key: string]: string; } | (({ target }: ChangeEvent) => void)'

How could I fix this? Thanks in advance!

Edit: I extract the values from 'values' with:

const [values, handleInputChange] = useForm({userID: '', password: ''})

Upvotes: 0

Views: 45

Answers (2)

Salvador Bacci
Salvador Bacci

Reputation: 79

I got it working using:

import { useState } from "react";

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

export const useForm = (initialState: InitialStateTypes): [InitialStateTypes, ({ target }: React.ChangeEvent<HTMLInputElement>) => void, ()=> void]  => {
  const [values, setValues] = useState(initialState);

  const reset = () => {
    setValues(initialState);
  }

  const handleInputChange = ({ target }: React.ChangeEvent<HTMLInputElement>) =>{
    setValues({
      ...values,
      [target.name]:target.value
    })
  }
  return [values, handleInputChange, reset]
}

And I can access the values from 'values' using values['value'] instead of values.value.

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191976

I would use generics to type the state according to the initial state, and return an object, so TS can easily infer the types (TS playground):

import React, { useState, ChangeEventHandler, ChangeEvent } from 'react';

export const useForm = <T extends Record<string, string>>( initialState: T ) => {
  const [values, setValues] = useState(initialState);

  const reset = () => {
    setValues(initialState);
  };

  const handleInputChange = ({ target }: ChangeEvent<HTMLInputElement>) => {
    setValues({
      ...values,
      [target.name]:target.value
    });
  };

  return { values, handleInputChange, reset };
}

const { values, handleInputChange } = useForm({ userID: '', password: '' });

values['userID'] = 'x';

values['password'] = 'y';

Upvotes: 2

Related Questions