Predicate/Guard Matching
Learn how to use predicate functions for flexible conditional logic and range matching.
Basic Predicate Matching
Use function predicates instead of literal values:
typescript
import { match } from '@anilkumarthakur/match'
const rating = (score: number): string => {
return match(score)
.on(
(n) => n >= 90,
() => '⭐⭐⭐⭐⭐'
)
.on(
(n) => n >= 80,
() => '⭐⭐⭐⭐'
)
.on(
(n) => n >= 70,
() => '⭐⭐⭐'
)
.on(
(n) => n >= 60,
() => '⭐⭐'
)
.on(
(n) => n >= 50,
() => '⭐'
)
.otherwise(() => '❌')
}
console.log(rating(95)) // "⭐⭐⭐⭐⭐"
console.log(rating(75)) // "⭐⭐⭐"
console.log(rating(45)) // "❌"Type Checking
Guard against different types using type checks:
typescript
const describe = (value: unknown): string => {
return match(value)
.on(
(v: unknown) => typeof v === 'string' && v.length > 0,
() => 'Non-empty string'
)
.on(
(v: unknown) => typeof v === 'string',
() => 'Empty string'
)
.on(
(v: unknown) => typeof v === 'number' && !isNaN(v),
() => 'Valid number'
)
.on(
(v: unknown) => typeof v === 'number',
() => 'NaN'
)
.on(
(v: unknown) => Array.isArray(v) && v.length > 0,
() => 'Non-empty array'
)
.on(
(v: unknown) => Array.isArray(v),
() => 'Empty array'
)
.on(
(v: unknown) => typeof v === 'object' && v !== null,
() => 'Object'
)
.on(
(v: unknown) => v === null,
() => 'Null'
)
.on(
(v: unknown) => v === undefined,
() => 'Undefined'
)
.otherwise(() => 'Unknown')
}
console.log(describe('hello')) // "Non-empty string"
console.log(describe('')) // "Empty string"
console.log(describe([1, 2, 3])) // "Non-empty array"
console.log(describe(null)) // "Null"Complex Business Logic
Use predicates for complex domain-specific logic:
typescript
interface Order {
total: number
itemCount: number
customerLevel: 'gold' | 'silver' | 'bronze'
isInternational: boolean
}
const calculateShipping = (order: Order): number => {
return match(order)
.on(
(o) => o.customerLevel === 'gold' && o.total > 100,
() => 0
) // Free
.on(
(o) => o.customerLevel === 'gold',
() => 5
) // Discounted
.on(
(o) => o.itemCount > 10 && o.isInternational,
() => 45
)
.on(
(o) => o.itemCount > 10,
() => 15
)
.on(
(o) => o.isInternational,
() => 35
)
.otherwise(() => 10)
}
const orders = [
{ total: 150, itemCount: 3, customerLevel: 'gold', isInternational: false },
{ total: 50, itemCount: 3, customerLevel: 'silver', isInternational: false },
{ total: 200, itemCount: 15, customerLevel: 'bronze', isInternational: true }
]
orders.forEach((order) => {
console.log(`Shipping: $${calculateShipping(order)}`)
})
// Shipping: $0
// Shipping: $10
// Shipping: $45Age-Based Classification
Common example: categorize by age ranges:
typescript
const ageGroup = (age: number): string => {
return match(age)
.on(
(n) => n < 0,
() => 'Invalid'
)
.on(
(n) => n < 13,
() => 'Child (0-12)'
)
.on(
(n) => n < 20,
() => 'Teen (13-19)'
)
.on(
(n) => n < 30,
() => 'Young Adult (20-29)'
)
.on(
(n) => n < 60,
() => 'Adult (30-59)'
)
.on(
(n) => n < 100,
() => 'Senior (60-99)'
)
.otherwise(() => 'Centenarian (100+)')
}
;[5, 15, 25, 45, 75, 105].forEach((age) => {
console.log(`Age ${age}: ${ageGroup(age)}`)
})
// Age 5: Child (0-12)
// Age 15: Teen (13-19)
// Age 25: Young Adult (20-29)
// Age 45: Adult (30-59)
// Age 75: Senior (60-99)
// Age 105: Centenarian (100+)HTTP Status Code Ranges
Match HTTP status codes by range:
typescript
const httpStatus = (code: number): string => {
return match(code)
.on(
(c) => c >= 200 && c < 300,
() => 'Success'
)
.on(
(c) => c >= 300 && c < 400,
() => 'Redirect'
)
.on(
(c) => c >= 400 && c < 500,
() => 'Client Error'
)
.on(
(c) => c >= 500 && c < 600,
() => 'Server Error'
)
.otherwise(() => 'Unknown')
}
console.log(httpStatus(200)) // "Success"
console.log(httpStatus(301)) // "Redirect"
console.log(httpStatus(404)) // "Client Error"
console.log(httpStatus(500)) // "Server Error"Array Length Guards
Use predicates with array operations:
typescript
const summarizeArray = (arr: unknown[]): string => {
return match(arr)
.on(
(a) => a.length === 0,
() => 'Empty array'
)
.on(
(a) => a.length === 1,
() => `Single item: ${arr[0]}`
)
.on(
(a) => a.length <= 5,
() => `Few items (${arr.length})`
)
.on(
(a) => a.length <= 100,
() => `Many items (${arr.length})`
)
.otherwise(() => `Huge array (${arr.length})`)
}
console.log(summarizeArray([])) // "Empty array"
console.log(summarizeArray([1])) // "Single item: 1"
console.log(summarizeArray([1, 2, 3])) // "Few items (3)"
console.log(summarizeArray(Array.from({ length: 1000 }, (_, i) => i))) // "Huge array (1000)"Mixing Literals and Predicates
Combine both for maximum flexibility:
typescript
interface Document {
type: 'pdf' | 'doc' | 'xls' | 'unknown'
size: number // in bytes
}
const documentStatus = (doc: Document): string => {
return match(doc)
.on(
(d) => d.type === 'pdf' && d.size > 10 * 1024 * 1024,
() => 'Large PDF'
)
.on('pdf', () => 'Small PDF')
.on(
(d) => d.size > 5 * 1024 * 1024,
() => 'Large file'
)
.on('doc', () => 'Document')
.on('xls', () => 'Spreadsheet')
.otherwise(() => 'Unknown format')
}
console.log(documentStatus({ type: 'pdf', size: 15 * 1024 * 1024 })) // "Large PDF"
console.log(documentStatus({ type: 'pdf', size: 2 * 1024 * 1024 })) // "Small PDF"
console.log(documentStatus({ type: 'doc', size: 1024 })) // "Document"String Pattern Matching
Use predicates with string methods:
typescript
const emailType = (email: string): string => {
return match(email)
.on(
(e) => e.includes('@gmail.com'),
() => 'Gmail'
)
.on(
(e) => e.includes('@outlook.com'),
() => 'Outlook'
)
.on(
(e) => e.includes('@company.com'),
() => 'Company Email'
)
.on(
(e) => /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(e),
() => 'Valid Email'
)
.otherwise(() => 'Invalid Email')
}
console.log(emailType('john@gmail.com')) // "Gmail"
console.log(emailType('jane@company.com')) // "Company Email"
console.log(emailType('bob@outlook.com')) // "Outlook"
console.log(emailType('invalid-email')) // "Invalid Email"FizzBuzz with Predicates
Classic FizzBuzz using match and predicates:
typescript
const fizzBuzz = (n: number): string => {
return match(n)
.on(
(x) => x % 15 === 0,
() => 'FizzBuzz'
)
.on(
(x) => x % 3 === 0,
() => 'Fizz'
)
.on(
(x) => x % 5 === 0,
() => 'Buzz'
)
.otherwise(() => String(n))
}
for (let i = 1; i <= 15; i++) {
console.log(fizzBuzz(i))
}
// 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz