final-form / final-form

🏁 Framework agnostic, high performance, subscription-based form state management

Home Page:https://final-form.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

form.change Typescript definition is too strict (fails for nested fields)

onlyann opened this issue · comments

Are you submitting a bug report or a feature request?

Bug report

What is the current behavior?

Assuming

type FormValue = {
  user: {
    name: string;
  },
  items: string[]
}

Both form.change("user.name") and form.change("items[0]") will result in

Argument of type 'string' is not assignable to parameter of type '"user" | "items"

What is the expected behavior?

This should be considered valid.

I suggest to revert #318
As far as I know, there is no way in Typescript to express the allowed syntax and string would be the best constraint for now.

Other information

@erikras I see that you had the same comment but somehow it got merged.
I am not sure to understand @yunyu 's answer.

Any update on this @erikras ? We also have the same issue with nested fields now.

In the linked PR (#318), @yunyu mentions a fallback.
However, it seems like this fallback is only used if no form type is provided at all!

So this will work:

const form = useForm();
form.change('some.nested.attribute')

But this will not:

const form = useForm<{some: {nested: {attribute: string}}}>();
form.change('some.nested.attribute', 'new value')

I think there are good reasons type the form and still use dot-syntax for accessors. At least for historic reasons.

If a typesafe API was to be provided, I think it would have to do something like this:

const form = useForm<{some: {nested: {attribute: string}}}>();
form.change((values) => values.some.nested.attribute, 'new value')

This was a major breaking change for us, and I would love to see it reverted.

It is totally possible to have type-safe access to nested paths, but you'd have to use arrays instead of a string for the path.

Here's one example in monocle-ts.

https://github.com/gcanti/monocle-ts/blob/a728cfc38472a10da24da27b964375634ee3d9d9/src/index.ts#L221

Earlier today I've tried to modify this library's code to accept array-based paths in addition to string-based ones, but found that to be very complicated and cumbersome as far as changing the internal functions to meet that. Some places are tightly coupled to the path being a string (e.g. sometimes the path is used to index some object, and that can't work with an array, of course). I've even tried creating a fromPath counterpart to toPath, but it ultimately ended up spurring some bugs I did not comprehend, even though the thing type-checked.

@erikras are you interested in changing the FormApi.change interfaces to also accept array paths? Like mentioned above, I tried my hand at it but couldn't fully flesh out an implementation which passed all the tests.

From my understanding that type definition linked above becomes much nicer with variadic types in TS 4.0 (releasing in a month).
https://devblogs.microsoft.com/typescript/announcing-typescript-4-0-beta/#variadic-tuple-types

Having strict types with arrays instead of (or in addition to) dot notation seems like a good direction.

@resolritter you might be able to coax TS into strongly typing path arrays, but good luck coaxing it into picking out the correct type for a nested field given the path, so that to can type check the values going to your validators and field components and so on. Last time I tried something similar to that lensing approach TS just seemed too weak to report errors on nested field types when it should. Hopefully this improves with newer versions of TS though.

bump.

I really want to upgrade, but this is getting in the way.

same issue as #391

and, with TS 4.3, maybe we can do some improvement on this: #391 (comment)