T
TanStackโ€ข13mo ago
fascinating-indigo

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
fascinating-indigo
fascinating-indigoOPโ€ข13mo 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>,
)
}
sensitive-blue
sensitive-blueโ€ข10mo 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
xenial-black
xenial-blackโ€ข10mo 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 ๐Ÿ™ .
continuing-cyan
continuing-cyanโ€ข10mo 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
sensitive-blue
sensitive-blueโ€ข10mo 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?