skip to Main Content

I’m new to TypeScript and I have been facing a typing issue and I have tried many ways but can’t get it fixed.

This is the error I’m getting:

Argument of type 'string | number' is not assignable to parameter of type 'SetStateAction<string>'.
  Type 'number' is not assignable to type 'SetStateAction<string>'.ts(2345)

The error occurs here:

  const [name, setName] = useState<string>('')

...

          <EditableField
            editing={editingName}
            setEditing={setEditingName}
            label="Name"
            type="text"
            value={name}
            setValue={name => setName(name)} /* HERE */
            displayValue={name}
            onBlur={onNameChange}
            ariaLabel="Edit Name"
          />

This is the definition of EditableField:

type EditableFieldProps = {
  editing: boolean
  setEditing: (editing: boolean) => void
  label: string
  type: string
  value: string | number
  setValue: (value: string | number) => void
  displayValue: string
  onBlur: () => void
  ariaLabel: string
}

const EditableField = ({ editing, setEditing, label, type, value, setValue, displayValue, onBlur, ariaLabel }: EditableFieldProps) => {
  const inputRef = useRef<HTMLInputElement | null>(null)

  useEffect(() => {
    if (editing) {
      setTimeout(() => {
        inputRef.current?.focus()
      }, 0)
    }
  }, [editing, inputRef])

  return (
    <>
      <Grid item xs={6}>
        <Typography variant="subtitle2" fontWeight="bold">{label}:</Typography>
      </Grid>
      <Grid item xs={6}>
        <Grid container alignItems="center">
          <Grid item flexGrow="1">
            {editing ? (
              <TextField
                label={label}
                type={type}
                {...(type === 'number' ? { inputProps: { step: '0.01' } } : {})}
                fullWidth
                value={value}
                onChange={e => setValue(e.target.value)}
                onBlur={onBlur}
                size="small"
                inputRef={inputRef}
                sx={{ marginTop: 1 }} />
            ) : (
              <Typography variant="subtitle2">{displayValue}</Typography>
            )}
          </Grid>
          <Grid item>
            {!editing && (
              <IconButton aria-label={ariaLabel} size="small" onClick={() => setEditing(true)}>
                <EditIcon fontSize="inherit" />
              </IconButton>
            )}
          </Grid>
        </Grid>
      </Grid>
    </>
  )
}

I have tried changing the value type of setValue to be SetStateAction<string | number> or SetStateAction | SetStateAction and still I get the issue.

2

Answers


  1. Chosen as BEST ANSWER

    I fixed it by using generics:

    type EditableFieldProps<T> = {
      editing: boolean
      setEditing: (editing: boolean) => void
      label: string
      type: string
      value: T
      setValue: (value: T) => void
      displayValue: string
      onBlur: () => void
      onEnter: () => void
      ariaLabel: string
    }
    
    function EditableField<T> ({ editing, setEditing, label, type, value, setValue, displayValue, onBlur, onEnter, ariaLabel }: EditableFieldProps<T>) {
      const inputRef = useRef<HTMLInputElement | null>(null)
    
      useEffect(() => {
        if (editing) {
          setTimeout(() => {
            inputRef.current?.focus()
          }, 0)
        }
      }, [editing, inputRef])
    
      return (
        <>
          <Grid item xs={6}>
            <Typography variant="subtitle2" fontWeight="bold">{label}:</Typography>
          </Grid>
          <Grid item xs={6}>
            <Grid container alignItems="center">
              <Grid item flexGrow="1">
                {editing ? (
                  <TextField
                    label={label}
                    type={type}
                    {...(type === 'number' ? { inputProps: { step: '0.01' } } : {})}
                    fullWidth
                    value={value}
                    onChange={e => setValue(e.target.value as T)}
                    onBlur={onBlur}
                    onKeyDown={e => {
                      if (e.key === 'Enter') {
                        onEnter()
                      }
                    }}
                    size="small"
                    inputRef={inputRef}
                    sx={{ marginTop: 1 }} />
                ) : (
                  <div onClick={() => setEditing(true)} style={{ cursor: 'pointer' }}>
                    <Typography variant="subtitle2">{displayValue}</Typography>
                  </div>
                )}
              </Grid>
              <Grid item>
                {!editing && (
                  <IconButton aria-label={ariaLabel} size="small" onClick={() => setEditing(true)}>
                    <EditIcon fontSize="inherit" />
                  </IconButton>
                )}
              </Grid>
            </Grid>
          </Grid>
        </>
      )
    }
    

    I'm not entirely happy with:

    onChange={e => setValue(e.target.value as T)}
    

    But, it works.


  2. To adjust the type of ‘setValue’ in your ‘EditableField’ component to accept ‘setStateAction<string | number>. Below you can see how to do it,

    type EditableFieldProps = {
    editing: boolean
    setEditing: (editing: boolean) => void
    label: string
    type: string
    value: string | number
    setValue: React.Dispatch<React.SetStateAction<string | number>>
    displayValue: string
    onBlur: () => void
    ariaLabel: string
    }
    
    const EditableField = ({ editing, setEditing, label, type, value, 
    setValue, displayValue, onBlur, ariaLabel }: EditableFieldProps) => {
    const inputRef = useRef<HTMLInputElement | null>(null)
    
    useEffect(() => {
    if (editing) {
      setTimeout(() => {
        inputRef.current?.focus()
      }, 0)
    }
    }, [editing, inputRef])
    
     return (
     <>
     <Grid item xs={6}>
     <Typography variant="subtitle2" fontWeight="bold">{label}:</Typography>
     </Grid>
      <Grid item xs={6}>
        <Grid container alignItems="center">
          <Grid item flexGrow="1">
            {editing ? (
              <TextField
                label={label}
                type={type}
                {...(type === 'number' ? { inputProps: { step: '0.01' } } : 
     {})}
                fullWidth
                value={value}
                onChange={e => setValue(e.target.value)}
                onBlur={onBlur}
                size="small"
                inputRef={inputRef}
                sx={{ marginTop: 1 }} />
            ) : (
              <Typography variant="subtitle2">{displayValue}</Typography>
            )}
          </Grid>
          <Grid item>
            {!editing && (
              <IconButton aria-label={ariaLabel} size="small" onClick={() => 
     setEditing(true)}>
                <EditIcon fontSize="inherit" />
              </IconButton>
            )}
          </Grid>
          </Grid>
          </Grid>
          </>
          )
         }
    

    With this changes the ‘setValue’ Prop in ‘EditableField’ should correctly accept a function that will update the state with either a string or a number.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search