Show form errors
Composes: ValidationError + PhoneNumber
This guide turns every ValidationError kind into user-facing copy with one exhaustive switch on the discriminated union. Validity is an accepting state of the automaton, and every ValidationError variant is a named way of not being in one, so the switch below covers everything the engine can report.
import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';import type { ValidationError } from '@telixon/core';
await ensureEngineReady();
function phoneErrorMessage(error: ValidationError): string { switch (error.kind) { case 'EMPTY': return 'Enter a phone number.'; case 'INVALID_CALLING_CODE': return 'The country code is not recognized. Check the digits after the +.'; case 'TOO_SHORT': return `The number is too short. Enter at least ${error.minLength} digits.`; case 'TOO_LONG': return `The number is too long. Enter at most ${error.maxLength} digits.`; case 'INVALID_LENGTH': return `The number has an impossible length. Valid lengths here: ${error.possibleLengths.join(' or ')} digits.`; case 'POSSIBLE_LOCAL_ONLY': return 'The number looks local. Include the area code.'; case 'PATTERN_MISMATCH': return 'The number does not match any pattern for this country.'; case 'NATIONAL_PREFIX_MISSING': return `Add the leading ${error.expectedPrefix} for domestic format.`; }}
const phone = parsePhoneNumber('415 555', { defaultRegion: 'US' });const error = phone.getValidationError();
console.log(error);// -> { kind: 'TOO_SHORT', minLength: 10 }
console.log(error ? phoneErrorMessage(error) : null);// -> The number is too short. Enter at least 10 digits.Running this prints the structured error and the mapped message shown in the comments. Because ValidationError is a discriminated union and the switch declares a string return type with no default, TypeScript fails the compile if any kind is left unhandled, so a metadata-driven copy gap cannot ship silently.
- Initialize the engine once at startup with
ensureEngineReady. - Parse the submitted value with
parsePhoneNumber. PassdefaultRegionwhen the input has no leading+. - Query
getValidationError()on the returnedPhoneNumber.nullmeans there is nothing to show. - Convert the error with
phoneErrorMessage, interpolating the payload fields (minLength,maxLength,possibleLengths,expectedPrefix) into the copy. - Decide your
NATIONAL_PREFIX_MISSINGpolicy. It is the only kind reported whileisValid()returnstrue, so surface it as a hint rather than a submit blocker. Gating submission itself is covered in Validate on submit.
All eight kinds
Section titled “All eight kinds”Every row below is an executed parse. Inputs without a region were parsed with no defaultRegion.
| Input | Region | Error | Message shown |
|---|---|---|---|
'' |
none | { kind: 'EMPTY' } |
Enter a phone number. |
'+999 123 4567' |
none | { kind: 'INVALID_CALLING_CODE' } |
The country code is not recognized. Check the digits after the +. |
'415 555' |
US |
{ kind: 'TOO_SHORT', minLength: 10 } |
The number is too short. Enter at least 10 digits. |
'(415) 555-01320' |
US |
{ kind: 'TOO_LONG', maxLength: 10 } |
The number is too long. Enter at most 10 digits. |
'+41 44 668 18 000' |
none | { kind: 'INVALID_LENGTH', possibleLengths: [9, 12] } |
The number has an impossible length. Valid lengths here: 9 or 12 digits. |
'555 0132' |
US |
{ kind: 'POSSIBLE_LOCAL_ONLY' } |
The number looks local. Include the area code. |
'+1 123 456 7890' |
none | { kind: 'PATTERN_MISMATCH' } |
The number does not match any pattern for this country. |
'7400 123456' |
GB |
{ kind: 'NATIONAL_PREFIX_MISSING', expectedPrefix: '0' } |
Add the leading 0 for domestic format. |
The Swiss row shows why INVALID_LENGTH is its own kind: the possible lengths for that number are 9 or 12 digits, so the 10-digit input is neither too short nor too long; it sits between them, and possibleLengths carries both values so the copy can name them.
The GB row is the one valid input in the table. isValid() returns true for it; the error tells the user the domestic form of an already-acceptable number. Every other kind implies isValid() is false.
Precedence
Section titled “Precedence”getValidationError() returns the first matching kind in a fixed order, so a value that is wrong in several ways reports exactly one error. Length checks run before pattern checks: an input with an impossible digit count reports TOO_SHORT, TOO_LONG, or INVALID_LENGTH and never reaches PATTERN_MISMATCH. The full precedence contract is specified on ValidationError.
Next steps
Section titled “Next steps”- Gate submission on the same parse result: Validate on submit
- Show errors while the user types instead of on submit: Drive an input
- The kind-by-kind contract and precedence order:
ValidationError