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')); // ❌ OtherP.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 fileP.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 messageP.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 onlyEmail 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 lengthP.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 tweetP.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 codeChaining 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
-
Chain predicates for complex validation:
P.string.minLength(8).includes('@').includes('.') -
Use regex for complex patterns:
P.string.regex(/^[A-Z]{2}\d{4}$/) // Two letters, four digits -
Combine with P.select() to extract values:
.with({ id: P.string.regex(/^\d+$/).select() }, (id) => ...) -
Consider performance: Simple predicates (startsWith, endsWith) are faster than regex
// Faster
P.string.startsWith('http')
// Slower but more flexible
P.string.regex(/^http/)