T
TanStackβ€’10mo ago
exotic-emerald

DeepKeys, only keys with a string value

Hi, I'm trying to make a reusable component that may receive a key into formdata but I want to constrain the component to only receive paths to string values. How can I achieve this? Right now I only have name set to DeepKeys<TFormData> which I guess is too broad.
import type { DeepKeys, ReactFormApi } from "@tanstack/react-form";
import type { ZodValidator } from "@tanstack/zod-form-adapter";
import { useId } from "react";
import { z } from "zod";
import { Input } from "./components/Input";
import { Label } from "./components/Label";

export const PasswordField = <TFormData,>({
form,
validatorKey,
name,
}: {
form: ReactFormApi<TFormData, ZodValidator>;
name: DeepKeys<TFormData>;
validatorKey: "onChange" | "onSubmit";
}) => {
const field = form.useField({
name: name,
validators: {
[validatorKey]: z
.string()
.min(8, "Password needs to have at least 8 characters."),
},
});

const fieldId = useId();

return (
<div className="mb-5">
<Label htmlFor={fieldId} className="mb-1">
Password
</Label>
<Input
id={fieldId}
type="password"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
className="mb-1"
aria-invalid={field.state.meta.errors.length > 0}
/>
{field.state.meta.errors.length > 0 && (
<div className="text-red-500 font-medium py-1">
{field.state.meta.errors[0]}
</div>
)}
</div>
);
};
import type { DeepKeys, ReactFormApi } from "@tanstack/react-form";
import type { ZodValidator } from "@tanstack/zod-form-adapter";
import { useId } from "react";
import { z } from "zod";
import { Input } from "./components/Input";
import { Label } from "./components/Label";

export const PasswordField = <TFormData,>({
form,
validatorKey,
name,
}: {
form: ReactFormApi<TFormData, ZodValidator>;
name: DeepKeys<TFormData>;
validatorKey: "onChange" | "onSubmit";
}) => {
const field = form.useField({
name: name,
validators: {
[validatorKey]: z
.string()
.min(8, "Password needs to have at least 8 characters."),
},
});

const fieldId = useId();

return (
<div className="mb-5">
<Label htmlFor={fieldId} className="mb-1">
Password
</Label>
<Input
id={fieldId}
type="password"
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
className="mb-1"
aria-invalid={field.state.meta.errors.length > 0}
/>
{field.state.meta.errors.length > 0 && (
<div className="text-red-500 font-medium py-1">
{field.state.meta.errors[0]}
</div>
)}
</div>
);
};
6 Replies
exotic-emerald
exotic-emeraldOPβ€’10mo ago
Right now I'm forced to cast these but I want to get rid of this casting to make TypeScript happy.
value={field.state.value as string}
onBlur={field.handleBlur}
onChange={(e) =>
field.handleChange(
e.target.value as DeepValue<TFormData, typeof name>,
)
}
value={field.state.value as string}
onBlur={field.handleBlur}
onChange={(e) =>
field.handleChange(
e.target.value as DeepValue<TFormData, typeof name>,
)
}
foreign-sapphire
foreign-sapphireβ€’7mo ago
came across this due to the same request, and I can confirm that this works! all checks outside of the component act as expected. Autocomplete for name seems to not work, but thatβ€˜s irrelevant as long as unexpected field values get rejected (which they do!) Thanks for the reference links Asked around for a more info on why extends unknown is needed. Looks like it can provide unexpected results with typescript at the moment. Specifically this comment: https://github.com/TanStack/form/pull/825/files#r1906897700 Also see this issue https://github.com/microsoft/TypeScript/issues/61242
fair-rose
fair-roseβ€’7mo ago
I've tried to implement the example from the PR #825 with the version 0.43.1, but now ReactFormApi and FieldOptions except respectively 9 and 10 type arguments πŸ˜…. I have no idea how to fix this, an udpated PR would be awesome πŸ™ .
like-gold
like-goldβ€’7mo ago
Yeah the last PR probably made this way a little bit harder than before, we can think about a way to make types easier from the outside.... oooor.... we can offer an alternative πŸ˜„ We're working on a nice and recommended approach to build reusable and composable components so that fighting with types shouldn't be a thing
foreign-sapphire
foreign-sapphireβ€’7mo ago
definitely. If you're messing with FieldApi, it'll be even more. 18, to be precise. perhaps this helps you to get along until the recommended approach is released Note that this breaks the component validators when accessed from outside.
No description
No description

Did you find this page helpful?