Skip to Content
String Predicates

Last Updated: 3/6/2026


String Predicates

P.string has several methods to match specific string patterns.

P.string.startsWith()

Matches strings that start with the provided string.

import { match, P } from 'ts-pattern'; const fn = (input: string) => match(input) .with(P.string.startsWith('TS'), () => '🎉 TypeScript related!') .with(P.string.startsWith('JS'), () => 'JavaScript related') .otherwise(() => '❌ Other'); console.log(fn('TS-Pattern')); // 🎉 TypeScript related! console.log(fn('JavaScript')); // ❌ Other

P.string.endsWith()

Matches strings that end with the provided string.

const fn = (input: string) => match(input) .with(P.string.endsWith('.ts'), () => 'TypeScript file') .with(P.string.endsWith('.js'), () => 'JavaScript file') .with(P.string.endsWith('.md'), () => 'Markdown file') .otherwise(() => 'Other file'); console.log(fn('index.ts')); // TypeScript file console.log(fn('README.md')); // Markdown file

P.string.includes()

Matches strings that contain the provided substring.

const fn = (input: string) => match(input) .with(P.string.includes('error'), () => '❌ Error message') .with(P.string.includes('warning'), () => '⚠️ Warning message') .with(P.string.includes('success'), () => '✅ Success message') .otherwise(() => 'Info message'); console.log(fn('Operation completed successfully')); // ✅ Success message

P.string.regex()

Matches strings against a regular expression.

const fn = (input: string) => match(input) .with(P.string.regex(/^[a-z]+$/), () => 'lowercase letters only') .with(P.string.regex(/^[A-Z]+$/), () => 'uppercase letters only') .with(P.string.regex(/^\d+$/), () => 'digits only') .otherwise(() => 'mixed content'); console.log(fn('hello')); // lowercase letters only console.log(fn('WORLD')); // uppercase letters only console.log(fn('123')); // digits only

Email Validation

const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; const validateEmail = (input: string) => match(input) .with(P.string.regex(emailRegex), (email) => ({ valid: true, email })) .otherwise(() => ({ valid: false })); console.log(validateEmail('user@example.com')); // { valid: true, email: 'user@example.com' }

P.string.minLength()

Matches strings with at least the specified number of characters.

const fn = (input: string) => match(input) .with(P.string.minLength(8), () => '✅ Strong password length') .otherwise(() => '❌ Password too short'); console.log(fn('secret')); // ❌ Password too short console.log(fn('verysecret')); // ✅ Strong password length

P.string.maxLength()

Matches strings with at most the specified number of characters.

const fn = (input: string) => match(input) .with(P.string.maxLength(280), () => '✅ Valid tweet') .otherwise(() => '❌ Tweet too long'); console.log(fn('Hello world!')); // ✅ Valid tweet

P.string.length()

Matches strings with exactly the specified number of characters.

const fn = (input: string) => match(input) .with(P.string.length(2), () => 'Two-letter code') .with(P.string.length(3), () => 'Three-letter code') .otherwise(() => 'Other length'); console.log(fn('US')); // Two-letter code console.log(fn('USA')); // Three-letter code

Chaining Predicates

String predicates can be chained together:

const fn = (input: string) => match(input) .with( P.string.minLength(8).includes('@').includes('.'), () => 'Potentially valid email' ) .otherwise(() => 'Invalid');

Combining with Other Patterns

With Objects

type User = { username: string; email: string; }; match(user) .with( { username: P.string.minLength(3).maxLength(20), email: P.string.includes('@'), }, (u) => ({ valid: true, user: u }) ) .otherwise(() => ({ valid: false }));

With P.select()

match(data) .with( { filename: P.string.endsWith('.ts').select() }, (filename) => { // filename: string (ending with .ts) return `TypeScript file: ${filename}`; } ) .otherwise(() => 'Other file');

With Arrays

match(files) .with( P.array(P.string.endsWith('.md')), (mdFiles) => `${mdFiles.length} markdown files` ) .otherwise(() => 'Mixed file types');

Real-World Examples

URL Router

type Route = string; const router = (route: Route) => match(route) .with(P.string.startsWith('/api/'), (r) => handleAPI(r)) .with(P.string.startsWith('/admin/'), (r) => handleAdmin(r)) .with(P.string.regex(/^\/blog\/\d+$/), (r) => handleBlogPost(r)) .otherwise((r) => handle404(r));

File Type Detection

const getFileType = (filename: string) => match(filename) .with( P.string.endsWith('.ts').or(P.string.endsWith('.tsx')), () => 'TypeScript' ) .with( P.string.endsWith('.js').or(P.string.endsWith('.jsx')), () => 'JavaScript' ) .with(P.string.endsWith('.json'), () => 'JSON') .with(P.string.endsWith('.md'), () => 'Markdown') .otherwise(() => 'Unknown');

Input Validation

type FormData = { username: string; password: string; email: string; }; const validateForm = (data: FormData) => match(data) .with( { username: P.string.minLength(3).maxLength(20).regex(/^[a-zA-Z0-9_]+$/), password: P.string.minLength(8), email: P.string.regex(/^[^\s@]+@[^\s@]+\.[^\s@]+$/), }, () => ({ valid: true }) ) .otherwise(() => ({ valid: false, error: 'Invalid input' }));

Log Level Parsing

const parseLogLevel = (log: string) => match(log) .with(P.string.startsWith('[ERROR]'), (l) => ({ level: 'error', message: l })) .with(P.string.startsWith('[WARN]'), (l) => ({ level: 'warn', message: l })) .with(P.string.startsWith('[INFO]'), (l) => ({ level: 'info', message: l })) .otherwise((l) => ({ level: 'debug', message: l }));

Best Practices

  1. Chain predicates for complex validation:

    P.string.minLength(8).includes('@').includes('.')
  2. Use regex for complex patterns:

    P.string.regex(/^[A-Z]{2}\d{4}$/) // Two letters, four digits
  3. Combine with P.select() to extract values:

    .with({ id: P.string.regex(/^\d+$/).select() }, (id) => ...)
  4. Consider performance: Simple predicates (startsWith, endsWith) are faster than regex

// Faster P.string.startsWith('http') // Slower but more flexible P.string.regex(/^http/)