Skip to content

TypeScript reference

This page lists every type exported from @samline/forms, what it represents, and where it shows up. Each type links to the method(s) that consume or produce it.

All types are exported from the package root:

import type {
AppendContentOptions,
AutoSubmitOptions,
FieldFormatConfig,
FieldFormatConfigMap,
FieldValidationContext,
FieldValidationRules,
FieldValidator,
FormController,
FormControllerOptions,
FormErrors,
FormFieldElement,
FormFieldValue,
FormsApi,
FormsAvailable,
FormStateListener,
FormStateSnapshot,
FormSubmitHandler,
FormTarget,
FormValues,
NewFormInput,
RuleConfig,
SerializedFormResult,
SerializedFormValue,
ValidationResult,
ValidationSchema,
VisualAttributes
} from '@samline/forms'

The full controller surface returned by form().

interface FormController {
readonly element: HTMLFormElement | null
readonly f: HTMLFormElement | null
readonly options: FormControllerOptions
onSubmit: (callback: FormSubmitHandler, preventDefault?: boolean) => FormController
watch: (field: string, callback: FormFieldWatcher) => FormController
observe: (field: string, callback: FormFieldWatcher) => () => void
unwatch: (field?: string, callback?: FormFieldWatcher) => FormController
subscribe: (listener: FormStateListener) => () => void
prefill: (fieldName?: string) => FormController
append: (options: AppendContentOptions) => HTMLElement | null
setErrors: (fields: string[] | FormErrors) => FormController
clearErrors: (fields?: string[]) => FormController
setValue: (name: string, value: unknown) => FormController
validate: (fields?: string[]) => ValidationResult
revalidate: (fields?: string[]) => ValidationResult
reset: () => FormController
autoSubmit: (options?: boolean | AutoSubmitOptions) => FormController
disableAutoSubmit: () => FormController
getValue: (name: string) => FormFieldValue
getField: (name: string) => FormFieldElement | FormFieldElement[] | null
getData: () => SerializedFormResult
getState: () => FormStateSnapshot
destroy: () => void
}

The second argument to form(). See Configuration for the full reference.

interface FormControllerOptions {
attributes?: Partial<VisualAttributes>
autoValidate?: boolean
autoSubmit?: boolean | AutoSubmitOptions
clearManualErrorsOnChange?: boolean
clearErrorsOnSubmit?: boolean
validators?: ValidationSchema
formats?: FieldFormatConfigMap
}

What form() accepts as its first argument.

type FormTarget =
| string // element id
| HTMLFormElement // direct element
| { current: HTMLFormElement | null } // ref-like
| null
| undefined

A string is treated as a document.getElementById(id) lookup; only HTMLFormElement matches count. A ref-like value lets you pass a Vue/React-style ref object.

The set of DOM field types the controller manages.

type FormFieldElement =
| HTMLInputElement
| HTMLSelectElement
| HTMLTextAreaElement

Returned by getField.

The normalized value of a field.

type FormFieldValue = string | string[] | File[] | undefined
  • string — for text inputs, textareas, single selects, and single checked checkboxes / radios.
  • string[] — for groups of checkboxes / radios where more than one is selected.
  • File[] — for <input type="file"> (may be empty).
  • undefined — when no field with that name exists.

Returned by getValue.

The aggregated values map produced by the controller and the helpers.

type FormValues = Record<string, FormFieldValue>

Useful when you build custom validators that need to read other fields:

form('signup-form', {
validators: {
confirm: {
validate: ({ value, values }) =>
value === values.password ? null : 'Passwords do not match.'
}
}
})

A map from field name to an array of error messages. Each field can have multiple errors (one per failed rule or per failing custom validator).

type FormErrors = Record<string, string[]>

Drives the css-error attribute on fields and is returned inside FormStateSnapshot and ValidationResult.

The shape returned by getState(). Built fresh on every call — it does not retain references to controller internals.

interface FormStateSnapshot {
values: FormValues
errors: FormErrors
filledFields: string[]
isValid: boolean
isValidated: boolean
autoSubmit: boolean
submitCount: number
}
Field Meaning
values Current values for every tracked field.
errors Merged validation and manual errors.
filledFields Names of fields that have a non-empty value.
isValid true when errors has no entries.
isValidated true once validate has run at least once.
autoSubmit true while auto-submit is enabled.
submitCount Number of submit attempts (valid or invalid).

The callback passed to subscribe.

type FormStateListener = (state: FormStateSnapshot) => void

Receives the current snapshot immediately on subscription, then on every state mutation.

The callback passed to onSubmit.

type FormSubmitHandler = (
form: HTMLFormElement,
data: Record<string, SerializedFormValue>,
formData: FormData,
state: FormStateSnapshot
) => void

Only invoked when the form is valid. data and formData are produced fresh on each invocation.

The callback passed to watch and observe.

type FormFieldWatcher = (
value: FormFieldValue,
field: FormFieldElement | FormFieldElement[] | null,
form: HTMLFormElement,
state: FormStateSnapshot
) => void
  • value — the current value of the field that just changed.
  • field — the DOM element(s) backing the field. Single matching field → HTMLInputElement / HTMLSelectElement / HTMLTextAreaElement; repeated fields → array; no match → null.
  • form — the bound HTMLFormElement.
  • state — a snapshot of the whole controller state.

observe fires this once immediately with the current value, then on every change. watch is a chainable alias and only fires on changes after it is registered.

The shape returned by getData() and parseFormData().

interface SerializedFormResult {
data: Record<string, SerializedFormValue>
formData: FormData
}

data is a plain object mirror of the FormData. Repeated names become arrays. Empty file entries are filtered out.

The value shape inside SerializedFormResult.data.

type SerializedFormValue = FormDataPrimitive | FormDataPrimitive[]
type FormDataPrimitive = FormDataEntryValue // string | File

The shape returned by validate, revalidate, and validateValues.

interface ValidationResult {
isValid: boolean
errors: FormErrors
}

errors is always a fresh object — safe to mutate.

The shape of options.validators.

type ValidationSchema = Record<string, FieldValidationRules>

The rule set for a single field.

interface FieldValidationRules {
required?: RuleConfig<boolean>
minLength?: RuleConfig<number>
maxLength?: RuleConfig<number>
pattern?: RuleConfig<RegExp>
validate?: FieldValidator | FieldValidator[]
}

Rules run in the order: requiredminLengthmaxLengthpatternvalidate. All are optional; an empty rules object contributes nothing.

Lets a rule be configured with a plain value or a { value, message } object.

type RuleConfig<T> = T | { value: T; message?: string }
form('signup-form', {
validators: {
password: {
minLength: { value: 8, message: 'Use at least 8 characters.' }
},
terms: {
required: { value: true, message: 'Please accept the terms.' }
}
}
})

When the plain form is used, default messages are produced automatically.

The argument passed to a custom validator.

interface FieldValidationContext {
field: string
value: FormFieldValue
values: FormValues
}

Use it to write cross-field validators:

form('checkout-form', {
validators: {
card: {
validate: ({ value, field }) => {
if (typeof value !== 'string') return `${field} is required.`
return /^\d{16}$/.test(value.replace(/\s+/g, ''))
? null
: 'Card number must be 16 digits.'
}
}
}
})

The custom validator signature.

type FieldValidator = (
context: FieldValidationContext
) => string | undefined | null | false | true
Return Meaning
string (non-empty) Push as the error message.
undefined, null, true Pass.
false Push the generic error message "Validation failed.".

Multiple validators can be chained by passing an array as validate:

form('order-form', {
validators: {
quantity: {
validate: [
({ value }) => (typeof value === 'string' && Number(value) > 0 ? null : 'Must be greater than zero.'),
({ value }) => (typeof value === 'string' && Number.isInteger(Number(value)) ? null : 'Must be an integer.')
]
}
}
})

The argument to autoSubmit() and the autoSubmit option.

interface AutoSubmitOptions {
debounce?: number
}

debounce is in milliseconds.

The argument to append().

interface AppendContentOptions {
tag: keyof HTMLElementTagNameMap
content: string
class?: string
atStart?: boolean
}
Field Meaning
tag The HTML tag to create.
content innerHTML content for the new node.
class Optional class name. If a node with the same first class already exists inside the form, it is removed before the new one is inserted.
atStart When true, inserts at the start of the form. Defaults to false (append at the end).

The shape of options.attributes.

interface VisualAttributes {
filled: string
error: string
}

Partial<VisualAttributes> is accepted, so you can override only one of them. See CSS styling.

FieldFormatConfig and FieldFormatConfigMap

Section titled “FieldFormatConfig and FieldFormatConfigMap”

The argument to format() and formatAll().

interface FieldFormatConfig {
type: FormatType
field: string | string[]
rawField?: string
options?: Record<string, unknown>
}
type FormatType =
| 'general'
| 'phone'
| 'numeral'
| 'date'
| 'time'
| 'creditCard'
| 'creditCardType'
interface FieldFormatConfigMap {
[field: string]: Omit<FieldFormatConfig, 'field'>
}

The field name is the name attribute of the visible input(s). rawField defaults to ${fieldName}Raw. options is forwarded to @samline/formatter — see its options reference.

The shape of the browser singleton exported from the vanilla entrypoint. The IIFE bundle exposes the same shape as window.Forms.

interface FormsApi {
form: (target: FormTarget, options?: FormControllerOptions) => FormController
newForm: (input: NewFormInput) => FormController | undefined
destroyForm: (id: string) => void
available: FormsAvailable
}

form is the same reference exported from @samline/forms. newForm and destroyForm manage the module-level available registry keyed by the form id. Spread browser into a new object to derive a fresh registry (with its own available map); see the getting-started guide for the pattern.

The argument to FormsApi.newForm.

interface NewFormInput {
id: string
options?: FormControllerOptions
}

A missing id logs Form ID is required to console.error and newForm returns undefined without touching the registry.

The shape of FormsApi.available.

interface FormsAvailable {
[id: string]: FormController
}

Use it to inspect or iterate over every live controller in the registry. Each entry is the FormController returned by newForm for that id.