TYPED DATA
A field has a type, and the type holds.
Sixteen types, a structure declared once, and the same truth from the schema to the screen.
Sixteen types, not seventeen
The list is closed, and that is what makes it usable: each type has its rendering, its filter, its validation and its own way of being understood by an agent. In a spreadsheet, a date is text that looks like a date, until someone types « end of March ». Here the type decides what can be entered, and the rest follows from it.
Text
Single line, or rich text with its formatting.
Number
A number, or an amount with its currency.
Choice
Status, single select, multi select, checkbox.
Time and place
Date, address.
Contact
Phone, workspace member.
Attachments
A document attached to the record.
Links
A relation to another record, single or multiple.
Computed
Formula on the record, rollup over linked records.
The type reaches the component
The schema builder accumulates types as fields are declared, so the record type is derived from its own definition. Making a field required flips it from optional to required in the inferred type, and whatever no longer fits breaks at compile time. Nobody hand-writes an interface restating what the schema already says, and which would eventually drift from it.
Validation comes out of the schema
Each type carries its validator and the object composes its own from them. You do not rewrite the input rules a second time for the form and a third time for the API. The validator is exposed as a Standard Schema, the shared specification across validation libraries: your forms consume it as is, with no bespoke adapter.
What is derived is not typed in
A formula draws its value from the other fields on the record. A rollup draws it from linked records: a client's invoice total comes from the invoices, not from a number copied across. These fields have no box to fill. A computation you can correct by hand becomes an ordinary field that looks like a computation, and nobody knows whether to trust it any more.
How it works
A premium amount, entered once, read everywhere.
The type is chosen. An amount with its currency, when the field is created.
Input is bounded. The type's validator refuses anything that is not an amount.
Rendering follows. The currency comes from the type's component, not recomposed by hand.
Computation becomes possible. A rollup sums those amounts. A string does not.