Type Definitions
md2form is written entirely in TypeScript, enabling type-safe form processing. This page provides detailed explanations of all type definitions.
v2 API Types
New types for parse results, options, and diagnostics were added in v2.
ParseResult
The return type of parseForm.
type ParseResult = {
document: FormDocument // Parsed form definition
diagnostics: Diagnostic[] // Error, warning, and information messages
ok: boolean // false if there are errors in strict mode
}ParseOptions
The options type passed to parseForm / createParser.
type ParseOptions = {
strict?: boolean // Default: false
defaultTitle?: string // Default: "Untitled Form"
validateSettings?: boolean // Default: true
}ParsedFormDocument
A branded type when ok === true.
type ParsedFormDocument = FormDocument & {
readonly __brand?: unique symbol
}Diagnostic
The type for individual diagnostic messages.
type Diagnostic = {
code: DiagnosticCode
severity: Severity
message: string
line?: number
column?: number
path?: string
}DiagnosticCode
type DiagnosticCode =
| "MISSING_FORM_TITLE"
| "MISSING_ELEMENT_TYPE"
| "UNSUPPORTED_ELEMENT_TYPE"
| "UNSUPPORTED_PROPERTY"
| "INVALID_PROPERTY_VALUE"
| "MISSING_REQUIRED_FIELD"
| "PROPERTY_BEFORE_TYPE"
| "ORPHAN_QUESTION"
| "AMBIGUOUS_STRUCTURE"
| "UNKNOWN_SETTING_KEY"
| "INVALID_SETTING_VALUE"Severity
type Severity = "error" | "warning" | "info"Parser
The return type of createParser.
type Parser = {
parse(markdown: string): ParseResult
}Form Definition Types
FormDocument
The main type representing an entire parsed form.
type FormDocument = {
schemaVersion?: number // Schema version (v2 = 2)
title: string // Form title (required)
description?: string // Form description (optional)
settings?: FormSettings // Form settings (optional)
pages: Page[] // Array of pages (sections)
}Usage Example:
import { parseForm, isParsedDocument } from "md2form"
import type { FormDocument } from "md2form"
const result = parseForm(markdown)
const form: FormDocument = result.document
console.log(form.title) // string
console.log(form.schemaVersion) // 2
console.log(form.pages.length) // numberFormSettings
Type representing form-wide settings configured in frontmatter.
type FormSettings = {
collectEmail?: boolean // Collect email addresses
allowMultipleResponses?: boolean // Allow multiple responses
limitResponses?: number | null // Limit number of responses
showProgressBar?: boolean // Show progress bar
shuffleQuestions?: boolean // Shuffle questions
themeColor?: string // Theme color
backgroundImage?: string // Background image
font?: string // Font
responseReceipt?: "always" | "never" | "whenRequested" // Response receipt
}Page
Type representing a single page (section) in a form.
type Page = {
title?: string // Page title (optional)
description?: string // Page description (optional)
elements: FormElement[] // Array of question elements
}FormElementBase
Base properties common to all question elements.
type FormElementBase = {
type: ElementType // Question type (required)
label?: string // Question title from ### heading (v2)
description?: string // Supplementary text after heading
required?: boolean // Required input flag
visible?: boolean // Visibility flag
}v2 Changes: The
labelfield stores the content of### question heading. In v1, the same value was indescription, but in v2descriptionis the supplementary paragraph text after the heading.
Question Type Enumeration
type ElementType =
| "short_text" // Short text input
| "long_text" // Long text input
| "number" // Numeric input
| "email" // Email address input
| "phone" // Phone number input
| "dropdown" // Dropdown selection
| "radio" // Radio button selection
| "checkbox" // Checkbox selection
| "date" // Date selection
| "time" // Time selection
| "rating" // Star rating
| "likert" // Likert scale
| "matrix" // Matrix (grid)
| "file_upload" // File upload
| "section_header" // Section header
| "scale" // Scale (slider)
| "signature" // Digital signature
| "image" // Image display
| "video" // Video display
| "boolean" // Yes/No selectionv2 Changes: The
"unknown"type from v1 was removed in v2. Unknown types are reported as diagnostics (UNSUPPORTED_ELEMENT_TYPE).
FormElement Union Type
A union type containing all question elements.
type FormElement =
| ShortText
| LongText
| NumberField
| EmailField
| PhoneField
| DropdownField
| RadioField
| CheckboxField
| DateField
| TimeField
| RatingField
| LikertField
| MatrixField
| FileUploadField
| SectionHeader
| ScaleField
| SignatureField
| MediaField
| BooleanFieldDetailed Question Type Definitions
Text Input
ShortText
type ShortText = FormElementBase & {
type: "short_text"
placeholder?: string // Placeholder text
maxLength?: number // Maximum number of characters
default?: string // Default value
}LongText
type LongText = FormElementBase & {
type: "long_text"
placeholder?: string // Placeholder text
maxLength?: number // Maximum number of characters
default?: string // Default value
richText?: boolean // Enable rich text editing
}NumberField
type NumberField = FormElementBase & {
type: "number"
placeholder?: string // Placeholder text
min?: number // Minimum value
max?: number // Maximum value
step?: number // Increment value
default?: number | null // Default value
integerOnly?: boolean // Allow only integers
}EmailField
type EmailField = FormElementBase & {
type: "email"
placeholder?: string // Placeholder text
default?: string // Default value
allowMultiple?: boolean // Allow multiple email addresses
}PhoneField
type PhoneField = FormElementBase & {
type: "phone"
placeholder?: string // Placeholder text
countryCodeRequired?: boolean // Require country code
default?: string // Default value
}Selection
DropdownField
type DropdownField = FormElementBase & {
type: "dropdown"
options: string[] // Choices
allowOther?: boolean // Allow "Other" choice
multiple?: false // Multiple selection (always false)
default?: string | null // Default selected value
searchable?: boolean // Enable search functionality
}RadioField
type RadioField = FormElementBase & {
type: "radio"
options: string[] // Choices
allowOther?: boolean // Allow "Other" choice
default?: string | null // Default selected value
}CheckboxField
type CheckboxField = FormElementBase & {
type: "checkbox"
options: string[] // Choices
minSelected?: number | null // Minimum number of selections
maxSelected?: number | null // Maximum number of selections
default?: string[] | null // Default selected values array
}Date/Time
DateField
type DateField = FormElementBase & {
type: "date"
includeTime?: boolean // Whether to include time
minDate?: string // Minimum date (ISO format)
maxDate?: string // Maximum date (ISO format)
default?: string | null // Default date
}TimeField
type TimeField = FormElementBase & {
type: "time"
minTime?: string // Minimum time (HH:MM)
maxTime?: string // Maximum time (HH:MM)
stepMinutes?: number // Increment (minutes)
default?: string | null // Default time
}Rating/Scale
RatingField
type RatingField = FormElementBase & {
type: "rating"
scale?: number // Number of rating levels
labels?: { low?: string; high?: string } // Lowest and highest labels
default?: number | null // Default rating
icon?: "star" | "heart" | "circle" // Icon type
}LikertField
type LikertField = FormElementBase & {
type: "likert"
statements: string[] // Rating items (rows)
scaleLabels: string[] // Rating scale (columns)
requiredPerStatement?: boolean // Rate each item as required
}MatrixField
type MatrixField = FormElementBase & {
type: "matrix"
rows: string[] // Row labels
columns: string[] // Column labels
cellType?: "radio" | "checkbox" | "number" | "short_text" // Cell type
requiredPerRow?: boolean // Each row requires input
}ScaleField
type ScaleField = FormElementBase & {
type: "scale"
min: number // Minimum value
max: number // Maximum value
step?: number // Increment value
minLabel?: string // Minimum value label
maxLabel?: string // Maximum value label
default?: number | null // Default value
}File/Signature
FileUploadField
type FileUploadField = FormElementBase & {
type: "file_upload"
allowedTypes?: string[] // Allowed file formats
maxFiles?: number // Maximum number of files
maxSizeMB?: number // Maximum file size (MB)
}SignatureField
type SignatureField = FormElementBase & {
type: "signature"
captureMode?: "draw" | "type" | "upload" // Signature capture method
required?: boolean // Required signature
}Media/Display
MediaField
type MediaField = FormElementBase & {
type: "image" | "video"
src: string // Media URL
alt?: string // Alternative text (image only)
width?: number | "auto" // Width
height?: number | "auto" // Height
caption?: string // Caption
}SectionHeader
type SectionHeader = FormElementBase & {
type: "section_header"
title?: string // Main title
subtitle?: string // Subtitle
}Other
BooleanField
type BooleanField = FormElementBase & {
type: "boolean"
onLabel?: string // Label for true state
offLabel?: string // Label for false state
default?: boolean | null // Default value
}v2 Changes: The
UnknownElementtype from v1 was removed in v2. Unknown#typevalues are reported asUNSUPPORTED_ELEMENT_TYPEdiagnostics.
Convenient Type Aliases
TextInputElement
Union type of text input elements:
type TextInputElement = ShortText | LongText | NumberField | EmailField | PhoneFieldType Guard Implementation
Type guard functions for safely processing form elements in TypeScript:
import type { FormElement, ShortText, NumberField, RatingField } from "md2form"
// Basic type guards
function isShortText(element: FormElement): element is ShortText {
return element.type === "short_text"
}
function isNumberField(element: FormElement): element is NumberField {
return element.type === "number"
}
function isRatingField(element: FormElement): element is RatingField {
return element.type === "rating"
}
// Generic type guard
function isElementOfType<T extends FormElement>(
element: FormElement,
type: T["type"],
): element is T {
return element.type === type
}
// Usage example
form.pages.forEach((page) => {
page.elements.forEach((element) => {
if (isShortText(element)) {
console.log(element.placeholder) // Type safe
}
if (isElementOfType<NumberField>(element, "number")) {
console.log(element.min, element.max) // Type safe
}
})
})Practical Type Usage Examples
Type-Safe Form Processing
import { parseForm, isParsedDocument } from "md2form"
import type { FormDocument, FormElement, ShortText, NumberField, CheckboxField } from "md2form"
// Usage example: parse and pass to FormProcessor
const result = parseForm(markdown, { strict: true })
if (!isParsedDocument(result)) throw new Error("Parse failed")
// Form processing class
class FormProcessor {
private form: FormDocument
constructor(form: FormDocument) {
this.form = form
}
// Type-safe element extraction
getElementsByType<T extends FormElement>(type: T["type"]): T[] {
const elements: T[] = []
this.form.pages.forEach((page) => {
page.elements.forEach((element) => {
if (element.type === type) {
elements.push(element as T)
}
})
})
return elements
}
// Get required elements
getRequiredElements(): FormElement[] {
const required: FormElement[] = []
this.form.pages.forEach((page) => {
page.elements.forEach((element) => {
if (element.required) {
required.push(element)
}
})
})
return required
}
// Get statistics
getStatistics(): {
totalElements: number
requiredElements: number
elementTypeCount: Record<string, number>
} {
let totalElements = 0
let requiredElements = 0
const elementTypeCount: Record<string, number> = {}
this.form.pages.forEach((page) => {
page.elements.forEach((element) => {
totalElements++
if (element.required) {
requiredElements++
}
elementTypeCount[element.type] = (elementTypeCount[element.type] || 0) + 1
})
})
return {
totalElements,
requiredElements,
elementTypeCount,
}
}
}Validation Functions
import type { FormElement, ShortText, LongText, NumberField, CheckboxField } from "md2form"
// Type-safe validation function
function validateFormElement(element: FormElement): string[] {
const errors: string[] = []
// Common validation
if (element.required && !element.label) {
errors.push("Required questions must have a label")
}
// Type-specific validation
switch (element.type) {
case "short_text":
case "long_text":
const textElement = element as ShortText | LongText
if (textElement.maxLength && textElement.maxLength <= 0) {
errors.push("maxLength must be a positive value")
}
break
case "number":
const numberElement = element as NumberField
if (
numberElement.min !== undefined &&
numberElement.max !== undefined &&
numberElement.min > numberElement.max
) {
errors.push("Minimum value must be less than or equal to maximum value")
}
break
case "checkbox":
const checkboxElement = element as CheckboxField
if (checkboxElement.options.length === 0) {
errors.push("Checkboxes must have choices")
}
if (
checkboxElement.maxSelected &&
checkboxElement.maxSelected > checkboxElement.options.length
) {
errors.push("Maximum selections must be less than or equal to number of choices")
}
break
}
return errors
}Custom Type Extension
import type { FormSettings, FormDocument } from "md2form"
// Extend custom settings type
interface ExtendedFormSettings extends FormSettings {
customBranding?: {
logo?: string
primaryColor?: string
secondaryColor?: string
}
analytics?: {
trackingId?: string
enableHeatmap?: boolean
}
}
// Extended form document type
interface ExtendedFormDocument extends Omit<FormDocument, "settings"> {
settings?: ExtendedFormSettings
}
// Usage example
function processExtendedForm(form: ExtendedFormDocument) {
console.log(form.title)
if (form.settings?.customBranding?.logo) {
console.log("Logo:", form.settings.customBranding.logo)
}
if (form.settings?.analytics?.trackingId) {
console.log("Tracking ID:", form.settings.analytics.trackingId)
}
}Benefits of Type Definitions
1. Development-Time Safety
import type { ShortText } from "md2form"
// Compile-time error detection
const element: ShortText = {
type: "short_text",
label: "Your Name",
maxLenght: 100, // ❌ Typo - compile error
}
// Correct usage
const element: ShortText = {
type: "short_text",
label: "Your Name",
maxLength: 100, // ✅ Correct
}2. IntelliSense Support
Auto-completion and documentation are available in the IDE.
3. Safe Refactoring
Type definitions enable safe refactoring even during large-scale changes.
JSON Type Definitions (Supplementary)
Auxiliary types related to JSON processing are defined in src/types/json.types.ts. These can also be imported directly from md2form:
import type { JsonValue, JsonObject, JsonArray } from "md2form"
// JSON value type definition
type JsonValue = string | number | boolean | null | JsonObject | JsonArray
interface JsonObject {
[key: string]: JsonValue
}
interface JsonArray extends Array<JsonValue> {}
// Type for converting form data to JSON
type SerializableFormDocument = Omit<FormDocument, "pages"> & {
pages: SerializableJsonObject[]
}Type Definition Best Practices
1. Explicit Type Annotations
import { parseForm, isParsedDocument } from "md2form"
import type { FormDocument } from "md2form"
const result = parseForm(markdown, { strict: true })
if (!isParsedDocument(result)) {
throw new Error("Parse failed")
}
// ✅ Explicit type annotation
const form: FormDocument = result.document2. Using Type Guards
import type { ShortText } from "md2form"
// ❌ Relying on type casting
const shortText = element as ShortText
console.log(shortText.maxLength)
// ✅ Using type guards
if (isShortText(element)) {
console.log(element.maxLength) // Type safe
}3. Proper Handling of Union Types
import type { FormElement } from "md2form"
// ✅ Using discriminated unions
function processElement(element: FormElement) {
switch (element.type) {
case "short_text":
// element is ShortText at this point
console.log(element.maxLength)
break
case "number":
// element is NumberField at this point
console.log(element.min, element.max)
break
// ... other cases
}
}Next Steps
After understanding type definitions, check the following pages for more detailed information:
- API Reference - Details of functions and classes
- Examples - Real-world usage examples in TypeScript
- Basics - Fundamentals of type-safe parsing