Typed schemas¶
buildFormFromSchema returns a FormGroup whose keys and control value types are
inferred from your schema literal. This only works when the schema keeps its
exact (“literal”) type. This page shows how to author schemas so inference holds —
and the one common mistake that silently throws it away.
Inference in action¶
Given a schema literal, the builder infers a precise FormGroup:
const schema = defineSchema({
kind: 'nodeGroup',
name: 'profile',
children: {
firstName: { kind: 'leaf', type: 'string', name: 'firstName' },
age: { kind: 'leaf', type: 'number', name: 'age' },
},
});
const form = buildFormFromSchema(schema);
// ^? FormGroup<{ firstName: FormControl<string>; age: FormControl<number> }>
form.controls.age.setValue('nope'); // ❌ compile error: expected number
form.getRawValue(); // { firstName: string; age: number }
The mistake that breaks it¶
Annotating the schema constant with : NodeGroup widens its type. The
children map collapses to Record<string, NodeType>, which erases the field
names and each field’s type — so the builder can no longer infer anything:
// ❌ inference lost
const schema: NodeGroup = {
kind: 'nodeGroup',
name: 'profile',
children: {
firstName: { kind: 'leaf', type: 'string', name: 'firstName' },
age: { kind: 'leaf', type: 'number', name: 'age' },
},
};
const form = buildFormFromSchema(schema);
// ^? FormGroup<{ [key: string]: AbstractControl }> ← names and types gone
The form still works at runtime — but you lose autocompletion, setValue type
checking, and the typed getRawValue().
The inferred types¶
For reference, these type aliases (exported from the package) drive the inference:
DFormControl<Node>— the control type for a single node.DFormGroup<Group>— theFormGrouptype for anodeGroup.LeafRuntimeType<Type>— maps a leaftypestring to its runtime type.
You rarely reference them directly; buildFormFromSchema applies them for you.