TanStackT
TanStack8mo ago
6 replies
faint-white

Reusable AppField components with unions

I have a multi-step form that's used for a union of data types that I'm discriminating by using a zod discriminated union schema. I've used this schema with .parse({}) to set up the default values and typescript. All of that works great. I use the form.Subscribe to get the values and use the values and the discriminator property to conditionally show fields. Awesome. Now, the issue I'm having is that the field values are not picking up the typing based on that narrowing. I'm not sure if I'm just doing something wrong. Here's the field in particular where I'm having the issue

type TextFieldProps = {
  label: string
  placeholder?: string
} & Omit<TextInputProps, 'label' | 'value' | 'onChange' | 'error' | 'name'>

export const TextField = ({ label, placeholder, ...props }: TextFieldProps) => {
  // The `Field` infers that it should have a `value` type of `string`
  const field = useFieldContext<string | number>()
  return (
    <TextInput
      label={label}
      placeholder={placeholder || ''}
      name={field.name}
      value={field.state.value}
      onChange={(e) => field.handleChange(e.target.value)}
      error={!field.state.meta.isValid && field.state.meta.errors.join(', ')}
      {...props}
    />
  )
}


Now when I use it in my form like so, I get a type error for my value that it's type "unknown" and I'm fairly certain it's because my field isn't picking up the values type narrowing which makes sense. Is there a way I can make it know that the state of the form was narrowed down somehow? Or do I just have to assert the type?

<form.AppField
  name='cost.amount'
  validators={{
    onSubmit: ({ value }) => !value && 'Enter a cost for this equipment',
    onChange: ({ value }) => (value && /^\d*$/.test(value) ? null : 'Cost must be numeric'),
  }}
>
  {({ TextField }) => <TextField label='Cost' required={true} autoComplete='off' />}
</form.AppField>
Was this page helpful?