#How do you manage fields with the type `Record<string, ...>` ?

28 messages · Page 1 of 1 (latest)

near dagger
#

I wonder if it is possible to have such field value with TanStack Form. Pretty sure it should (and is) but the types seem wrong:

function App() {
  const form = createForm(() => ({
    defaultValues: {
      firstName: '',
      lastName: '',
      address: {
        home: {
          street: '',
          number: '',
        },
      } as Record<string, { street: string; number: string }>,
    },
    ...
  }));

  return (
    <div>
      <h1>Simple Form Example</h1>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          e.stopPropagation();
          form.handleSubmit();
        }}
      >
        ...
        <div>
          <form.Field
            name={`address.${'home'}.street`}
            children={(field) => (
              <>
                <label for={field().name}>Street:</label>
                <input
                  id={field().name}
                  name={field().name}
                  value={field().state.value}
                  onBlur={field().handleBlur}
                  onInput={(e) => field().handleChange(e.target.value)}
                />
                <FieldInfo field={field()} />
              </>
            )}
          />
        </div>

In this example, I specify the field name name={`address.${'home'}.street`} where home is a variable in my real use case.

If you open this Stackblitz repro, you'll notice this does not work as expected:
https://stackblitz.com/edit/ts-form-object?file=src%2Findex.tsx&preset=node

There is no type issue on the name attribute of form.Field as it respect one of the expected value (address.${string}.street), although the type of the value itself is wrong:

(property) value: {
    street: string;
    number: string;
} & string

So is the handleChange definition.

Am I doing something wrong? Is it not supported (and therefore should I need to change the structure of my form)?

Run official live example code for Form Simple, created by Tanstack on StackBlitz

#

This "works":

          <form.Field
            name={`address.${"home"}`}
            children={(field) => (
              <>
                <label for={field().name}>Street:</label>
                <input
                  id={field().name}
                  name={field().name}
                  value={field().state.value.street}
                  onBlur={field().handleBlur}
                  onInput={(e) => field().handleChange({...field().state.value, street: e.target.value})}
                />
                <FieldInfo field={field()} />
              </>
            )}
          />

It seems the value and handleChange types match the first ${string} occurrence. Not sure if this is expected.

sharp sandal
#

Records should be supported, and that type looks wrong.

#

address.${'home'}.street should be a valid template string to enter

near dagger
#

mmh

#

does the Stackblitz highlights an issue then ?

sharp sandal
#

I'll take a look. The stackblitz seems short enough, so a unit test should be easy to derive

near dagger
#

awesome

#

if required i can open an issue on github for you

sharp sandal
#

this bug seems familiar ... the intersection with string I mean

near dagger
#

yeah the intersection with both the type of address.home and address.home.street looks suspicious ;D

sharp sandal
#

maybe it's solid-specific. I just checked my React prod implementation using a record and the type is just fine there

#

nope, it's not. Could you create a Github issue with your stackblitz reproduction, please? @near dagger

near dagger
#

sure I will !

#

(I wasn't inspired for the name of the issue, sorry ^^)

sharp sandal
#

thanks!

near dagger
#

you're welcome, thank YOU for the nice work !

sharp sandal
#

so any subfield from a record gets caught in an intersection with the record value

near dagger
#

yeah that's what I noticed

#

you are mentioning typescript. does it means it's not solvable with the current typescript implementation?

near dagger
#

I can live with the type issue. my understanding is that the functionality itself exists in TS Form, which is good enough for me 😉

#

I can just cast for the time being

sharp sandal
#

the reason it wasn't caught is because the iteration doesn't throw a type error, even though the type it generated can't be created

#

For reference:

type Records = {
    [x: `foo.${string}`]: {
        bar: string;
    };
    [x: `foo.${string}.bar`]: string;
//  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// '`foo.${string}.bar`' index type 'string' is not assignable to '`foo.${string}`' index type '{ bar: string; }'.
    foo: Record<string, {
        bar: string;
    }>;
}
near dagger
#

han