validateEmail
validateEmail normalizes and validates an email address.
It returns the original input string when valid and undefined when invalid.
This helper is:
- Pure and side‑effect‑free.
- Intention‑revealing.
- Explicit about strict vs loose behavior.
- Designed for predictable, real‑world subset of email syntax validation.
Use this helper when you need a safe, conservative, and well‑defined email validator.
Function Signature
validateEmail(value: unknown, strict?: boolean): string | undefined
Parameters
| Name | Data type | Description |
|---|---|---|
| value | unknown |
The email address to validate. |
| strict | boolean |
Specifies whether to enforce strict mode for validation. |
Normalization
Uses normalizeString: - Only native strings are accepted. - Leading/trailing whitespace is trimmed. - Empty strings are invalid. - No coercion is performed.
Normalization is used only for validation. The original input string is returned on success.
Strict Mode (default)
Strict mode enforces a conservative, real‑world subset of email syntax.
Local Part Rules
- Must match:
/^[A-Za-z0-9]+(?:[._%+-][A-Za-z0-9]+)*$/. - No leading dots.
- No trailing dots.
- No consecutive dots.
- No whitespace.
- Max length: 64 characters.
Strict mode rejects
- Quoted local parts.
- Escaped characters.
- Rare or legacy RFC constructs.
Domain Rules
- Max length: 253 characters.
- Split on
.into labels. - Each label:
- 1–63 characters.
- Must match:
/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?$/ - No whitespace.
- No empty labels.
- TLD must be alphabetic, 2–63 characters.
Strict mode is designed for predictable, production‑safe email formats.
Loose Mode
Loose mode allows a broader set of valid RFC 5322 addresses while preserving safety.
Local Part Rules
Loose mode allows:
- Quoted local parts.
- Escaped characters inside quotes.
- Whitespace inside quoted local parts.
Loose mode uses:
/^(?:[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\.)*")$/
Whitespace Rules in Loose Mode
- Whitespace inside "quoted local parts" is allowed.
- Whitespace outside quotes is rejected.
- Domain whitespace is always rejected.
Domain Rules
Same as strict mode:
- No whitespace.
- No empty labels.
- No invalid characters.
- TLD must be alphabetic.
Loose mode expands local‑part flexibility without weakening domain safety.
Return Value
- Returns the original input string when valid.
- Returns
undefinedwhen invalid.
This preserves user‑facing fidelity and avoids accidental mutation.
Error Handling
This validator:
- Never throws.
- Never coerces.
- Never mutates input.
- Returns
undefinedfor all invalid cases.
This aligns with Jane’s philosophy of predictable, side‑effect‑free helpers.
Design Rationale
Why strict vs loose mode?
Email syntax is extremely permissive in RFC 5322, but most real‑world systems accept only a small, predictable subset.
- Strict mode enforces that subset.
- Loose mode allows rare but valid forms without compromising safety.
Why no control‑character regex?
- ESLint’s no-control-regex rule prohibits control ranges in literals.
- Instead of fighting the rule, the validator enforces safety through:
- Whitespace rules
- Structural validation
- Explicit character sets
This keeps the implementation simple, lint‑clean, and intention‑revealing.
Why return the original string?
Normalization is used only for validation.
Returning the original preserves fidelity and avoids surprising the caller.
Why explicit domain label validation?
Domain correctness is structural, not regex‑driven.
Explicit label rules are easier to reason about, maintain, and test.
Example Usage
validateEmail('user@example.com');
// → "user@example.com"
validateEmail('"user name"@example.com', false);
// → "\"user name\"@example.com"
validateEmail('invalid@exa_mple.com');
// → undefined