CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-io-ts-types

A collection of codecs and combinators for use with io-ts

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

branded-types-validation.mddocs/

Branded Types and Validation

Branded types that add compile-time safety and runtime validation for special string formats and constraints. These codecs provide type-safe wrappers around primitive values with additional semantic meaning and validation rules.

Capabilities

NonEmptyString

A branded type that ensures strings are non-empty at both compile-time and runtime, providing safer string handling.

/**
 * Brand interface for non-empty strings
 */
interface NonEmptyStringBrand {
  readonly NonEmptyString: unique symbol;
}

/**
 * Branded type for non-empty strings
 */
type NonEmptyString = t.Branded<string, NonEmptyStringBrand>;

/**
 * Codec interface for NonEmptyString
 */
interface NonEmptyStringC extends t.Type<NonEmptyString, string, unknown> {}

/**
 * Codec that validates strings are non-empty (length > 0)
 * Provides compile-time and runtime safety for non-empty strings
 */
const NonEmptyString: NonEmptyStringC;

Usage Examples:

import { NonEmptyString } from "io-ts-types";
import { isRight } from "fp-ts/lib/Either";

const result1 = NonEmptyString.decode("hello");
// Right("hello" as NonEmptyString)

const result2 = NonEmptyString.decode("");
// Left([ValidationError]) - empty string not allowed

const result3 = NonEmptyString.decode("   ");
// Right("   " as NonEmptyString) - whitespace is allowed

// Type safety at compile time
function processName(name: NonEmptyString) {
  // Guaranteed to be non-empty at compile time
  return `Hello, ${name}!`;
}

if (isRight(result1)) {
  processName(result1.right); // Type-safe usage
}

// Encoding back to string
const encoded = NonEmptyString.encode(result1.right);
// "hello" (regular string)

UUID

A branded type for UUID strings with runtime validation against the standard UUID format using regex pattern matching.

/**
 * Brand interface for UUID strings
 */
interface UUIDBrand {
  readonly UUID: unique symbol;
}

/**
 * Branded type for UUID strings
 */
type UUID = t.Branded<string, UUIDBrand>;

/**
 * Codec that validates strings match UUID format using regex pattern
 * Supports standard UUID formats (8-4-4-4-12 hexadecimal digits)
 */
const UUID: t.Type<UUID, string, unknown>;

Usage Examples:

import { UUID } from "io-ts-types";
import { isRight } from "fp-ts/lib/Either";

const result1 = UUID.decode("550e8400-e29b-41d4-a716-446655440000");
// Right("550e8400-e29b-41d4-a716-446655440000" as UUID)

const result2 = UUID.decode("invalid-uuid");
// Left([ValidationError]) - doesn't match UUID pattern

const result3 = UUID.decode("550e8400e29b41d4a716446655440000");
// Left([ValidationError]) - missing hyphens

const result4 = UUID.decode("");
// Left([ValidationError]) - empty string

// Type safety for UUID usage
function getUserById(id: UUID) {
  // Guaranteed to be a valid UUID format
  return database.users.findById(id);
}

if (isRight(result1)) {
  getUserById(result1.right); // Type-safe UUID usage
}

// Encoding back to string
const encoded = UUID.encode(result1.right);
// "550e8400-e29b-41d4-a716-446655440000" (regular string)

RegExp Validation

Validates values are RegExp instances using proper type checking, useful for runtime validation of regular expressions.

/**
 * Codec interface for RegExp validation
 */
interface RegExpC extends t.Type<RegExp, RegExp, unknown> {}

/**
 * Codec that validates values are RegExp instances
 * Uses Object.prototype.toString check for accurate RegExp detection
 */
const regexp: RegExpC;

Usage Examples:

import { regexp } from "io-ts-types";

const result1 = regexp.decode(/^[a-z]+$/);
// Right(/^[a-z]+$/)

const result2 = regexp.decode(new RegExp("\\d+"));
// Right(/\d+/)

const result3 = regexp.decode("string-pattern");
// Left([ValidationError]) - string is not a RegExp

const result4 = regexp.decode(/invalid[/);
// Right(regexp) - even invalid patterns are RegExp instances

// Identity encoding (RegExp -> RegExp)
const pattern = /^test$/;
const encoded = regexp.encode(pattern);
// Same RegExp object

Common Usage Patterns

User Input Validation

import * as t from "io-ts-types";
import { NonEmptyString, UUID } from "io-ts-types";

const UserProfile = t.type({
  id: UUID,
  username: NonEmptyString,
  displayName: NonEmptyString,
  email: t.string, // Regular email validation would go here
  bio: t.union([NonEmptyString, t.null])
});

const profileData = {
  id: "550e8400-e29b-41d4-a716-446655440000",
  username: "alice_dev",
  displayName: "Alice Developer", 
  email: "alice@example.com",
  bio: "Full-stack developer passionate about TypeScript"
};

const validated = UserProfile.decode(profileData);
// Right({ id: UUID, username: NonEmptyString, displayName: NonEmptyString, ... })

API Request Validation

import * as t from "io-ts-types";
import { UUID, NonEmptyString } from "io-ts-types";

const CreateTaskRequest = t.type({
  projectId: UUID,
  title: NonEmptyString,
  description: t.union([NonEmptyString, t.null]),
  assigneeId: t.union([UUID, t.null])
});

const requestBody = {
  projectId: "123e4567-e89b-12d3-a456-426614174000",
  title: "Implement user authentication",
  description: "Add JWT-based authentication system",
  assigneeId: "550e8400-e29b-41d4-a716-446655440000"
};

const validated = CreateTaskRequest.decode(requestBody);
// Ensures projectId and assigneeId are valid UUIDs, title is non-empty

Database Entity Validation

import * as t from "io-ts-types";
import { UUID, NonEmptyString } from "io-ts-types";

const Article = t.type({
  id: UUID,
  slug: NonEmptyString,
  title: NonEmptyString,
  content: NonEmptyString,
  authorId: UUID,
  tags: t.array(NonEmptyString),
  published: t.boolean
});

const articleData = {
  id: "article-123e4567-e89b-12d3-a456-426614174000",
  slug: "typescript-branded-types",
  title: "Understanding TypeScript Branded Types", 
  content: "Branded types provide compile-time safety...",
  authorId: "author-550e8400-e29b-41d4-a716-446655440000",
  tags: ["typescript", "type-safety", "branded-types"],
  published: true
};

const validated = Article.decode(articleData);
// Validates UUIDs, ensures strings are non-empty, validates array of non-empty tags

Configuration Validation

import * as t from "io-ts-types";
import { NonEmptyString, UUID } from "io-ts-types";

const DatabaseConfig = t.type({
  host: NonEmptyString,
  port: t.number,
  database: NonEmptyString,
  username: NonEmptyString,
  password: NonEmptyString,
  connectionId: t.union([UUID, t.null])
});

const config = {
  host: "localhost",
  port: 5432,
  database: "myapp_production",
  username: "dbuser",
  password: "secure_password_123",
  connectionId: "conn-123e4567-e89b-12d3-a456-426614174000"
};

const validated = DatabaseConfig.decode(config);
// Ensures all string fields are non-empty, validates UUID format for connectionId

Form Validation with Branded Types

import * as t from "io-ts-types";
import { NonEmptyString } from "io-ts-types";
import { pipe } from "fp-ts/lib/function";
import { fold } from "fp-ts/lib/Either";

const ContactForm = t.type({
  name: NonEmptyString,
  subject: NonEmptyString,
  message: NonEmptyString,
  email: t.string // Could use an EmailString branded type
});

function validateContactForm(formData: unknown) {
  return pipe(
    ContactForm.decode(formData),
    fold(
      (errors) => ({ success: false, errors }),
      (data) => ({ success: true, data })
    )
  );
}

const formSubmission = {
  name: "John Doe",
  subject: "Question about your service",
  message: "I would like to know more about...",
  email: "john@example.com"
};

const result = validateContactForm(formSubmission);
// { success: true, data: { name: NonEmptyString, subject: NonEmptyString, ... } }

const invalidSubmission = {
  name: "",  // Empty name will fail
  subject: "Question",
  message: "Hello",
  email: "john@example.com"
};

const invalidResult = validateContactForm(invalidSubmission);
// { success: false, errors: [ValidationError[]] }

Runtime Pattern Validation

import { regexp } from "io-ts-types";

const PatternConfig = t.type({
  emailPattern: regexp,
  phonePattern: regexp,
  usernamePattern: regexp
});

const patterns = {
  emailPattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
  phonePattern: /^\+?[\d\s\-\(\)]+$/,
  usernamePattern: /^[a-zA-Z0-9_]{3,20}$/
};

const validated = PatternConfig.decode(patterns);
// Right({ emailPattern: RegExp, phonePattern: RegExp, usernamePattern: RegExp })

// Use validated patterns safely
if (validated._tag === "Right") {
  const { emailPattern, phonePattern, usernamePattern } = validated.right;
  
  const isValidEmail = emailPattern.test("user@example.com"); // true
  const isValidPhone = phonePattern.test("+1 (555) 123-4567"); // true
  const isValidUsername = usernamePattern.test("user_123"); // true
}

docs

branded-types-validation.md

collection-codecs.md

date-handling.md

functional-programming-types.md

index.md

json-handling.md

string-number-transformations.md

utility-functions-codec-modifiers.md

tile.json