At the moment, to set a field value, you have to call a method with the following signature:
setFieldValue(field: string, value: any)
When using Formik with TypeScript, this method isn't "safe": you could specify a field that doesn't exist, or specify a value that doesn't confirm to the type signature.
A setFieldValue (or similar) method that takes a "partial" object would allow for writing a TypeScript definition similar to setState, for example:
setFieldValue({ name: "Sam", zipcode: "12345" })
And a definition like (pseudocode):
setFieldValue<K extends keyof Values>(value: (Pick<Values, K> | Values | null)): void;
I know that TypeScript isn't given first-class support by the maintainers, but I'm interested in knowing your thoughts on such a setFieldValue method: I would be happy to raise a PR with this change and write the associated TypeScript definitions if you think this is a good idea.
TypeScript users.
I couldn't think of any, but I'd be interested to know if there's an alternative.
That new signature could work nicely, but I suggest it just be defined as another callback, aptly named setFieldValues (plural).
The current method of setFieldValue needs its signature fixed. The field parameter should be restricted so its only a key of the Values generic. I'm not sure why its just plainly defined as a string, but its concerning to me.
setFieldValue(field: keyof Values, value: any): void;
@justinbhopper That makes sense: and you're right, setFieldValues is a much better name. I might give this a shot, if people feel like it's a sensible idea.
I certainly would use it. I think this would be the signature you would use:
export interface FormikActions<Values> {
setFieldValues(values: Partial<Values>): void;
}
You'd roll through each property in values one by one, probably using a for in. You should accept the value, even if its undefined -- don't make the mistake of skipping/ignoring undefined values, because currently formik allows setting a field's value to undefined in setFieldValue.
We ran into this in a project we're working on as well. I think this signature might work for setFieldValue (making the "value" typesafe as well):
export interface FormikActions<Values> {
setFieldValue<Field extends keyof Values>(
field: Field,
value: Values[Field],
shouldValidate?: boolean
): void;
}
this is stale?
Definitely not stale, but keyof implementations are _extremely broken_. This needs to support nested fields. See work at https://github.com/johnrom/formik-typed for example of a workaround. Combined with extending #1336 to all of Formik itself (it currently just strengthens fields, and is way out of date) would make it work, but unfortunately it uses a Proxy and Proxies are not supported in IE, so not many real world projects could make use of it.
Other ideas on implementations:
interface Person {
name: {
first: string,
last: string,
}
friends: Person[];
}
const index = 0;
//
// this is what I'd like to see, but I have no idea how it could be implemented without proxies.
//
setFieldValues<Person>(values => values.friends[index].name.first)("john");
//
// these would be limited by the number of type aliases we wanted to support
// and be extremely hard to maintain in typescript
//
setFieldValue<Person>("friends", index, "name", "first")("john"));
// linq-esque
setFieldValue<Person>(
() => "friends",
() => index,
() => "name",
() => "first"
)("john");
//
// these are ugly as heck
//
setFieldValue<Person>(() => "friends")(() => index)(() => "name")(() => "first")("john");
// why am I still trying
setFieldValue<Person>(() => [
"friends,
() => [
index,
() => [
"name",
() =>
"first"
]
]
])("john");
//
// these are proxies so not supported in IE
//
// this one is basically https://github.com/johnrom/formik-typed
() =>
<Formik<Person>>
{fields => fields.friends[0].name.first._setValue("john");}
</Formik>;
// also kinda ugly and weird
setFieldValue(getField<Person>().friends[0].name.first._getName(), "john");
Other than that, the best type-safe-ish implementation is:
// name is not type safe
const [field, meta, helpers] = useField<string>("friends[0].name.first");
// but setValue is!
helpers.setValue(0); // ERROR: should be a string
Welcome to other ideas, but none of these APIs are something I'd personally be interested in implementing until proxies are usable on the web.
Most helpful comment
We ran into this in a project we're working on as well. I think this signature might work for
setFieldValue(making the "value" typesafe as well):