A collection of codecs and combinators for use with io-ts
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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)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)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 objectimport * 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, ... })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-emptyimport * 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 tagsimport * 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 connectionIdimport * 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[]] }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
}