0
# TypeScript Support
1
2
Utility functions and type helpers for enhanced TypeScript support with strongly typed form and field components.
3
4
## Capabilities
5
6
### withTypes Utility
7
8
Utility function that provides strongly typed Form and FormSpy components for specific form value types.
9
10
```typescript { .api }
11
/**
12
* Creates strongly typed Form and FormSpy components for specific form values
13
* @returns Object with typed Form and FormSpy components
14
*/
15
function withTypes<FormValues = Record<string, any>>(): {
16
Form: React.ComponentType<FormProps<FormValues>>;
17
FormSpy: React.ComponentType<FormSpyProps<FormValues>>;
18
};
19
```
20
21
**Usage Examples:**
22
23
```typescript
24
import { withTypes } from "react-final-form";
25
26
// Define your form values interface
27
interface UserFormValues {
28
firstName: string;
29
lastName: string;
30
email: string;
31
age: number;
32
isActive: boolean;
33
}
34
35
// Create strongly typed components
36
const { Form, FormSpy } = withTypes<UserFormValues>();
37
38
// Now Form and FormSpy are fully typed for UserFormValues
39
function TypedUserForm() {
40
const onSubmit = (values: UserFormValues) => {
41
// values is fully typed as UserFormValues
42
console.log(values.firstName); // TypeScript knows this is a string
43
console.log(values.age); // TypeScript knows this is a number
44
};
45
46
return (
47
<Form
48
onSubmit={onSubmit}
49
initialValues={{
50
firstName: "John", // Type-checked
51
lastName: "Doe", // Type-checked
52
email: "john@example.com",
53
age: 30,
54
isActive: true
55
}}
56
render={({ handleSubmit, values }) => (
57
<form onSubmit={handleSubmit}>
58
{/* values is typed as UserFormValues */}
59
<div>Name: {values.firstName} {values.lastName}</div>
60
61
<FormSpy>
62
{({ values: spyValues }) => (
63
<div>
64
{/* spyValues is also typed as UserFormValues */}
65
Email: {spyValues.email}
66
Age: {spyValues.age}
67
</div>
68
)}
69
</FormSpy>
70
71
<button type="submit">Submit</button>
72
</form>
73
)}
74
/>
75
);
76
}
77
```
78
79
### Version Information
80
81
Access to the current version of react-final-form.
82
83
```typescript { .api }
84
/**
85
* Current version of react-final-form
86
*/
87
const version: string;
88
```
89
90
**Usage Example:**
91
92
```typescript
93
import { version } from "react-final-form";
94
95
function VersionInfo() {
96
return <div>React Final Form version: {version}</div>;
97
}
98
```
99
100
### TypeScript Type Definitions
101
102
Complete type definitions for all components, hooks, and interfaces.
103
104
```typescript { .api }
105
/**
106
* Core form component type
107
*/
108
type FormComponent<FormValues = Record<string, any>> = React.ComponentType<
109
FormProps<FormValues>
110
>;
111
112
/**
113
* Core field component type
114
*/
115
type FieldComponent<
116
FieldValue = any,
117
T extends HTMLElement = HTMLElement,
118
FormValues = Record<string, any>
119
> = React.ComponentType<FieldProps<FieldValue, T, FormValues>>;
120
121
/**
122
* FormSpy component type
123
*/
124
type FormSpyComponent<FormValues = Record<string, any>> = React.ComponentType<
125
FormSpyProps<FormValues>
126
>;
127
128
/**
129
* Generic render prop function type
130
*/
131
type RenderFunction<T> = (props: T) => React.ReactNode;
132
133
/**
134
* Form render prop function type
135
*/
136
type FormRenderFunction<FormValues = Record<string, any>> = RenderFunction<
137
FormRenderProps<FormValues>
138
>;
139
140
/**
141
* Field render prop function type
142
*/
143
type FieldRenderFunction<
144
FieldValue = any,
145
T extends HTMLElement = HTMLElement,
146
FormValues = any
147
> = RenderFunction<FieldRenderProps<FieldValue, T, FormValues>>;
148
149
/**
150
* FormSpy render prop function type
151
*/
152
type FormSpyRenderFunction<FormValues = Record<string, any>> = RenderFunction<
153
FormSpyRenderProps<FormValues>
154
>;
155
```
156
157
### Advanced TypeScript Patterns
158
159
Comprehensive examples of advanced TypeScript usage with react-final-form.
160
161
**Usage Examples:**
162
163
```typescript
164
import React from "react";
165
import { Form, Field, FormSpy, withTypes } from "react-final-form";
166
167
// Complex form values interface
168
interface ComplexFormValues {
169
personalInfo: {
170
firstName: string;
171
lastName: string;
172
dateOfBirth: Date;
173
};
174
contactInfo: {
175
email: string;
176
phone?: string;
177
address: {
178
street: string;
179
city: string;
180
zipCode: string;
181
};
182
};
183
preferences: {
184
newsletter: boolean;
185
theme: "light" | "dark";
186
notifications: string[];
187
};
188
}
189
190
// Create typed components
191
const { Form: TypedForm, FormSpy: TypedFormSpy } = withTypes<ComplexFormValues>();
192
193
// Typed field component
194
function TypedField<K extends keyof ComplexFormValues>({
195
name,
196
...props
197
}: {
198
name: K;
199
} & React.InputHTMLAttributes<HTMLInputElement>) {
200
return (
201
<Field name={name as string}>
202
{({ input, meta }) => (
203
<div>
204
<input {...input} {...props} />
205
{meta.error && meta.touched && <span>{meta.error}</span>}
206
</div>
207
)}
208
</Field>
209
);
210
}
211
212
// Generic field component with type inference
213
function GenericField<T extends HTMLElement = HTMLInputElement>({
214
name,
215
component = "input" as any,
216
...props
217
}: {
218
name: string;
219
component?: keyof JSX.IntrinsicElements | React.ComponentType<any>;
220
} & React.HTMLAttributes<T>) {
221
return (
222
<Field name={name} component={component} {...props} />
223
);
224
}
225
226
// Strongly typed form validation
227
function validateComplexForm(values: Partial<ComplexFormValues>) {
228
const errors: Partial<Record<keyof ComplexFormValues, any>> = {};
229
230
if (!values.personalInfo?.firstName) {
231
errors.personalInfo = { firstName: "Required" };
232
}
233
234
if (!values.contactInfo?.email) {
235
errors.contactInfo = { email: "Required" };
236
}
237
238
return errors;
239
}
240
241
// Complete typed form
242
function ComplexTypedForm() {
243
const onSubmit = (values: ComplexFormValues) => {
244
// All properties are type-safe
245
console.log(values.personalInfo.firstName);
246
console.log(values.contactInfo.email);
247
console.log(values.preferences.theme);
248
};
249
250
return (
251
<TypedForm
252
onSubmit={onSubmit}
253
validate={validateComplexForm}
254
initialValues={{
255
personalInfo: {
256
firstName: "",
257
lastName: "",
258
dateOfBirth: new Date()
259
},
260
contactInfo: {
261
email: "",
262
address: {
263
street: "",
264
city: "",
265
zipCode: ""
266
}
267
},
268
preferences: {
269
newsletter: false,
270
theme: "light",
271
notifications: []
272
}
273
}}
274
render={({ handleSubmit, values }) => (
275
<form onSubmit={handleSubmit}>
276
<TypedField name="personalInfo" placeholder="This would need proper nested handling" />
277
278
<TypedFormSpy>
279
{({ values: formValues }) => (
280
<div>
281
Theme: {formValues.preferences?.theme}
282
Newsletter: {formValues.preferences?.newsletter ? "Yes" : "No"}
283
</div>
284
)}
285
</TypedFormSpy>
286
287
<button type="submit">Submit</button>
288
</form>
289
)}
290
/>
291
);
292
}
293
```
294
295
### Type Utilities and Helpers
296
297
Additional type utilities for working with form state and field values.
298
299
```typescript { .api }
300
/**
301
* Extract field value type from form values
302
*/
303
type FieldValueType<
304
FormValues,
305
FieldName extends keyof FormValues
306
> = FormValues[FieldName];
307
308
/**
309
* Create partial form values type for updates
310
*/
311
type PartialFormValues<FormValues> = Partial<FormValues>;
312
313
/**
314
* Form values with all fields optional (for initial values)
315
*/
316
type InitialFormValues<FormValues> = Partial<FormValues>;
317
318
/**
319
* Extract render props type for specific component
320
*/
321
type ExtractRenderProps<T> = T extends React.ComponentType<infer P>
322
? P extends { render?: (props: infer R) => any }
323
? R
324
: never
325
: never;
326
327
/**
328
* Type-safe field name extractor
329
*/
330
type FieldNames<FormValues> = keyof FormValues | string;
331
332
/**
333
* Validation error type for form values
334
*/
335
type FormErrors<FormValues> = Partial<Record<keyof FormValues, any>>;
336
337
/**
338
* Field validation function type
339
*/
340
type FieldValidationFunction<FieldValue, FormValues = any> = (
341
value: FieldValue,
342
allValues: FormValues,
343
meta?: any
344
) => any | Promise<any>;
345
346
/**
347
* Form validation function type
348
*/
349
type FormValidationFunction<FormValues> = (
350
values: FormValues
351
) => FormErrors<FormValues> | Promise<FormErrors<FormValues>>;
352
```
353
354
**Usage Examples:**
355
356
```typescript
357
// Using type utilities
358
interface UserForm {
359
name: string;
360
email: string;
361
age: number;
362
}
363
364
// Extract specific field type
365
type NameFieldType = FieldValueType<UserForm, "name">; // string
366
type AgeFieldType = FieldValueType<UserForm, "age">; // number
367
368
// Create validation functions with proper typing
369
const validateName: FieldValidationFunction<string, UserForm> = (
370
value,
371
allValues,
372
meta
373
) => {
374
if (!value) return "Name is required";
375
if (value.length < 2) return "Name must be at least 2 characters";
376
return undefined;
377
};
378
379
const validateUserForm: FormValidationFunction<UserForm> = (values) => {
380
const errors: FormErrors<UserForm> = {};
381
382
if (!values.name) errors.name = "Required";
383
if (!values.email) errors.email = "Required";
384
if (values.age < 18) errors.age = "Must be 18 or older";
385
386
return errors;
387
};
388
389
// Type-safe field names
390
const userFormFields: (keyof UserForm)[] = ["name", "email", "age"];
391
```
392
393
### Integration with External Type Systems
394
395
Examples of integrating react-final-form with popular TypeScript libraries.
396
397
**Usage Examples:**
398
399
```typescript
400
// Integration with Zod schema validation
401
import { z } from "zod";
402
403
const userSchema = z.object({
404
name: z.string().min(1, "Name is required"),
405
email: z.string().email("Invalid email"),
406
age: z.number().min(18, "Must be 18 or older")
407
});
408
409
type UserFormValues = z.infer<typeof userSchema>;
410
411
const validateWithZod = (values: UserFormValues) => {
412
try {
413
userSchema.parse(values);
414
return {};
415
} catch (error) {
416
if (error instanceof z.ZodError) {
417
return error.formErrors.fieldErrors;
418
}
419
return {};
420
}
421
};
422
423
// Integration with react-hook-form-like patterns
424
function createTypedForm<T>() {
425
return {
426
Form: withTypes<T>().Form,
427
FormSpy: withTypes<T>().FormSpy,
428
useFormState: () => useFormState<T>(),
429
useForm: () => useForm<T>()
430
};
431
}
432
433
const userFormComponents = createTypedForm<UserFormValues>();
434
```