T
TanStack4mo ago
exotic-emerald

Lack of Type Safety with Pre-bound Field Components?

https://tanstack.com/form/latest/docs/framework/react/guides/form-composition#pre-bound-field-components This documentation suggests that pre-bound field components "infer" that such a field should assume that the value in the form state has the same type as the type argument to useFieldContext. However, this means that whenever I'm actually using the pre-bound field, there are no guardrails preventing me from using a pre-bound field with a form value of a different type. Demonstrating in code, it seems like a big danger that I can do this:
<form.AppField name="number">
{(field) => (
<field.String />
)}
</form.AppField>
<form.AppField name="number">
{(field) => (
<field.String />
)}
</form.AppField>
Is this something I can actually assert? Or if not are there plans to get around it?
Form Composition | TanStack Form React Docs
A common criticism of TanStack Form is its verbosity out-of-the-box. While this can be useful for educational purposes helping enforce understanding our APIs it's not ideal in production use cases. As...
22 Replies
like-gold
like-gold4mo ago
you can assert it if you make the component generic and pass the field for typing purposes in this case you want to assert that typeof field = { state: { value: string } }
exotic-emerald
exotic-emeraldOP4mo ago
Yes, that's what I want to assert. Interesting... you mean like this?:
<field.String field={field} />
<field.String field={field} />
like-gold
like-gold4mo ago
or perhaps <field.String<typeof field> > works too. Haven‘t tried it before but yes, for type important field components I do what you sent
exotic-emerald
exotic-emeraldOP4mo ago
Got it. I wish this were somehow bound by default. I guess if I pass as a prop, then I don't need to use useFieldContext at all. I don't want any of my pre-bound fields to be used without this type restriction I'll do this for now though, thanks!
like-gold
like-gold4mo ago
the context manages the 18 generics for you, which you wouldn‘t have with that prop I mentioned
exotic-emerald
exotic-emeraldOP4mo ago
The typing comes out as FieldApi<any, string, ActualTypeThatMatters, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any> so I am not sure that it matters? Yeah, actually not having much luck accomplishing the typing of such a prop nvm got it, just had to make the key generic as well
interface TanstackField<TData, TKey extends string>
extends FieldApi<
any,
TKey,
TData,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any
> {}
interface TanstackField<TData, TKey extends string>
extends FieldApi<
any,
TKey,
TData,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any
> {}
fun!
environmental-rose
environmental-rose4mo ago
The suggested fix to this problem is:
<form.AppField name="number">
{(field) => (
<field.String value={field.state.value} />
)}
</form.AppField>
<form.AppField name="number">
{(field) => (
<field.String value={field.state.value} />
)}
</form.AppField>
Even if field.state.value goes unused That way String is just defined as:
function String(props: {value: string}) {}
function String(props: {value: string}) {}
exotic-emerald
exotic-emeraldOP3mo ago
Ah, that's a bit cleaner. I'm not encountering any real issue with passing the field reference
environmental-rose
environmental-rose3mo ago
Yet. 😉 We do not assure the consistency of generics in our code. We may add or remove many in the future In even patch versions of the library
optimistic-gold
optimistic-gold3mo ago
If I keep updating the generics to match the library, is there any risk to using the field reference approach?
interface TanstackField<TData>
extends FieldApi<
any,
any,
TData,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any
> {}

export function TextField(props: {
field: TanstackField<string | null | undefined>;
}): ReactNode;
interface TanstackField<TData>
extends FieldApi<
any,
any,
TData,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any
> {}

export function TextField(props: {
field: TanstackField<string | null | undefined>;
}): ReactNode;
It feels very clean Better yet, is it possible that a future version of the library itself exports a TanstackField<TData> equivalent type? 😁 😁 😁
environmental-rose
environmental-rose3mo ago
Nope! It broadly goes against our philosophy of requiring generics for our core usage There isn't, nah, just a game of cat and mouse with generics updates And it becomes a major headache on our end to support the "why doesn't inheritance work when using this generic type" type of tickets. After all, it's a limit from TS itself not to have partial inheritance - otherwise we would likely enable that API
optimistic-gold
optimistic-gold3mo ago
I see! Though I don't fully understand the inheritance problem implications, I'm glad both approaches will remain viable. 😁
like-gold
like-gold3mo ago
if I understood it right, partial inheritance would be * "The first generic parameter is given, infer the rest from the options" While TypeScript currently either wants all the parameters, parameters with defaults or inferring all of them
optimistic-gold
optimistic-gold3mo ago
What use-cases would that enable in practice? In my case, wanting reusable form field components, I only care about TData no? Others being any is fine
like-gold
like-gold3mo ago
Type safety through generics while not requiring the user to define all 18. You'd allow the first one to be manually set, and the rest would still have the ability to infer their values from the options but as it is right now with TypeScript, you either define them all or infer all of them which is essentially what you were asking for here. You want the first one to be set while preserving the behaviour of the others
optimistic-gold
optimistic-gold3mo ago
I actually just meant the exact type alias I sent above! Just maintained by the library instead of me "AnyFieldApi" already exists I just meant a "AnyFieldApiWithData<T>" with any everywhere except TData haha
like-gold
like-gold3mo ago
useFieldContext is already implemented and accepts one optional generic so yeah, if you want the single generic for type reasons, then what crutchcorn suggested also works
correct-apricot
correct-apricot3mo ago
While I appreciate TanStack Form's type-safe and verbose approach, I feel these advantages are lost when using context composition APIs for complex forms, as everything tends to become any typed. This makes me wonder about the utility of the verbosity. The withForm HOC is an option, but it seems better suited for larger form sections rather than individual fields. I've also tried a helper type approach, but working with generics has proven too complex.
environmental-rose
environmental-rose3mo ago
We're working on a brand new API to address many of these concerns
environmental-rose
environmental-rose3mo ago
GitHub
feat(react-form): Add withFormLens by LeCarbonator · Pull Reques...
This is not confirmed to be added. This is an opportunity to get feedback on this suggestion and/or implementation. Todos - [ ] Is this concept okay to begin with? It&amp;#39;s more related to disc...
environmental-rose
environmental-rose3mo ago
Feedback welcome/encouraged Get it in now before we go live 😄
correct-apricot
correct-apricot3mo ago
Didn't saw it coming, amazing!

Did you find this page helpful?