🚀 Big News:Socket Has Acquired Secure Annex.Learn More →
Socket
Book a DemoSign in
Socket

@anilkumarthakur/match

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@anilkumarthakur/match

PHP-style match expressions for JavaScript/TypeScript

latest
Source
npmnpm
Version
0.1.3
Version published
Maintainers
1
Created
Source

@anilkumarthakur/match

npm version license tests coverage

PHP-style match expressions for JavaScript/TypeScript with 100% type safety and comprehensive test coverage.

@anilkumarthakur/match brings the power and elegance of PHP's match expression to the JavaScript/TypeScript world. It provides a clean, type-safe alternative to complex switch statements and nested if-else logic.

Features

✨ Type-Safe: Full TypeScript support with generic types for subject and result
🎯 Readable: Clean, expressive syntax inspired by PHP match expressions
🚀 Fast: Efficient equality-based matching using JavaScript's Map
📦 Lightweight: Zero dependencies, only ~0.5KB gzipped (ES module)
đź§Ş Well-Tested: 127 comprehensive tests with 100% code coverage
đź”— Chainable: Fluent API for method chaining
🌍 Cross-Platform: Works in Node.js and browsers (ESM + UMD)

Installation

npm

npm install @anilkumarthakur/match

yarn

yarn add @anilkumarthakur/match

bun

bun add @anilkumarthakur/match

Quick Start

import { match } from '@anilkumarthakur/match'

const result = match('success')
  .on('success', () => 'Operation successful!')
  .on('error', () => 'Something went wrong')
  .otherwise(() => 'Unknown status')

console.log(result) // "Operation successful!"

API Reference

match<TSubject, TResult>(subject: TSubject): Matcher

Creates a new match expression for the given subject value.

Parameters:

  • subject - The value to match against (any type)

Returns: A Matcher instance for method chaining

Example:

const matcher = match(statusCode)

on(value: TSubject, handler: () => TResult): Matcher

Adds a case to match against. Uses strict equality (===) for comparison.

Parameters:

  • value - The value to match
  • handler - Function returning the result if matched

Returns: The matcher instance (for chaining)

Example:

match(status)
  .on(200, () => 'Success')
  .on(404, () => 'Not Found')

onAny(values: readonly TSubject[], handler: () => TResult): Matcher

Adds multiple values that all map to the same handler (simulates PHP's comma-separated cases).

Parameters:

  • values - Array of values to match
  • handler - Function to execute if any value matches

Returns: The matcher instance (for chaining)

Example:

match(status)
  .onAny([200, 201, 202], () => 'Success')
  .onAny([400, 401, 403], () => 'Client Error')

otherwise(handler: () => TResult): TResult

Sets the default handler and executes the match. Returns immediately with the result.

Parameters:

  • handler - Function to execute if no cases match

Returns: The result from matched handler or default handler

Throws: UnhandledMatchError if no match found and no default provided

Example:

const result = match(value)
  .on('expected', () => 'matched')
  .otherwise(() => 'default')

default(handler: () => TResult): TResult

PHP-compatible alias for otherwise(). Identical behavior.

Example:

const result = match(value)
  .on('expected', () => 'matched')
  .default(() => 'default')

valueOf(): TResult

Executes the match without a default handler. Throws if no match found.

Returns: The result from matched handler

Throws: UnhandledMatchError if no match found

Example:

const result = match('test')
  .on('test', () => 'matched')
  .valueOf() // Must have matched something

UnhandledMatchError

Custom error thrown when no case matches and no default handler is provided.

Properties:

  • name - "UnhandledMatchError"
  • message - Contains the unmatched value

Example:

try {
  match('foo')
    .on('bar', () => 'bar')
    .valueOf()
} catch (error) {
  if (error instanceof UnhandledMatchError) {
    console.error('No match found for:', error.message)
  }
}

Usage Examples

Basic String Matching

import { match } from '@anilkumarthakur/match'

const getRole = (role: string) => {
  return match(role)
    .on('admin', () => 'Full access')
    .on('user', () => 'Limited access')
    .on('guest', () => 'Read-only access')
    .otherwise(() => 'Unknown role')
}

console.log(getRole('admin')) // "Full access"

Number Matching (HTTP Status Codes)

const handleResponse = (statusCode: number) => {
  return match(statusCode)
    .on(200, () => 'OK')
    .onAny([201, 202, 204], () => 'Created/Accepted')
    .on(400, () => 'Bad Request')
    .on(401, () => 'Unauthorized')
    .on(404, () => 'Not Found')
    .on(500, () => 'Server Error')
    .otherwise(() => 'Unknown Status')
}

console.log(handleResponse(200)) // "OK"
console.log(handleResponse(201)) // "Created/Accepted"
console.log(handleResponse(999)) // "Unknown Status"

Complex Notifications

const showNotification = (type: string, message: string) => {
  const styling = match(type)
    .on('success', () => ({ color: 'green', icon: 'âś“' }))
    .on('error', () => ({ color: 'red', icon: 'âś—' }))
    .on('warning', () => ({ color: 'orange', icon: 'âš ' }))
    .on('info', () => ({ color: 'blue', icon: 'ℹ' }))
    .otherwise(() => ({ color: 'gray', icon: '•' }))

  return `[${styling.icon}] ${message}`
}

console.log(showNotification('success', 'Saved!')) // "[âś“] Saved!"
console.log(showNotification('error', 'Failed!')) // "[âś—] Failed!"

Nested Match Expressions

const getUserStatus = (userId: string, status: string) => {
  return match(userId)
    .on('admin', () => {
      return match(status)
        .on('active', () => 'Admin is active')
        .on('inactive', () => 'Admin is inactive')
        .otherwise(() => 'Admin status unknown')
    })
    .on('user', () => {
      return match(status)
        .on('active', () => 'User is active')
        .otherwise(() => 'User is inactive')
    })
    .otherwise(() => 'User not found')
}

console.log(getUserStatus('admin', 'active')) // "Admin is active"
console.log(getUserStatus('user', 'active')) // "User is active"
console.log(getUserStatus('guest', 'active')) // "User not found"

Type-Safe Unions

type LogLevel = 'debug' | 'info' | 'warn' | 'error'

const getLogColor = (level: LogLevel): string => {
  return match(level)
    .on('debug', () => 'gray')
    .on('info', () => 'blue')
    .on('warn', () => 'yellow')
    .on('error', () => 'red')
    .otherwise(() => 'white')
}

console.log(getLogColor('info')) // "blue"

Conditional Logic with match(true)

const getUserMessage = (age: number, isPremium: boolean) => {
  return match(true)
    .on(age < 13, () => 'Not eligible')
    .on(age >= 13 && age < 18, () => 'Teen user')
    .on(age >= 18 && !isPremium, () => 'Free user')
    .on(age >= 18 && isPremium, () => 'Premium user')
    .otherwise(() => 'Unknown')
}

console.log(getUserMessage(25, true)) // "Premium user"
console.log(getUserMessage(16, false)) // "Teen user"

Days in Month (Real-World Example)

const daysInMonth = (month: string, year: number): number => {
  const isLeap = (y: number) => y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)

  return match(month.toLowerCase().slice(0, 3))
    .on('jan', () => 31)
    .on('feb', () => (isLeap(year) ? 29 : 28))
    .on('mar', () => 31)
    .on('apr', () => 30)
    .on('may', () => 31)
    .on('jun', () => 30)
    .on('jul', () => 31)
    .on('aug', () => 31)
    .on('sep', () => 30)
    .on('oct', () => 31)
    .on('nov', () => 30)
    .on('dec', () => 31)
    .otherwise(() => {
      throw new Error('Invalid month')
    })
}

console.log(daysInMonth('February', 2024)) // 29 (leap year)
console.log(daysInMonth('February', 2025)) // 28

Comparison with PHP match()

PHP

$result = match($status) {
    'success', 'ok' => 'All good',
    'error', 'fail' => 'Something went wrong',
    default => 'Unknown'
};

JavaScript (this library)

const result = match(status)
  .onAny(['success', 'ok'], () => 'All good')
  .onAny(['error', 'fail'], () => 'Something went wrong')
  .otherwise(() => 'Unknown')

Supported Types

The library supports matching on any JavaScript type using strict equality (===):

  • âś… Strings
  • âś… Numbers (including Infinity, -Infinity)
  • âś… Booleans
  • âś… null / undefined
  • âś… Symbols
  • âś… BigInt
  • âś… Objects (by reference)
  • âś… Arrays (by reference)
  • âś… Functions (by reference)
  • âś… Enums
  • âś… Class instances (by reference)

Type Safety

Full TypeScript support with automatic type inference:

// Explicit types
const result = match<string, number>('test')
  .on('test', () => 123)
  .otherwise(() => 456)

// Inferred types
const result2 = match('test')
  .on('test', () => 'result') // Inferred as string result
  .otherwise(() => 'default')

// Union types
type Status = 'success' | 'pending' | 'error'
const result3 = match<Status, string>('success')
  .on('success', () => 'Done')
  .on('pending', () => 'In progress')
  .on('error', () => 'Failed')
  .otherwise(() => 'Unknown')

Performance

  • ⚡ Uses JavaScript Map for O(1) lookup time
  • đź’ľ Lazy evaluation - only matched handler executes
  • 📦 Bundle sizes: ES (1.03 kB / 0.46 kB gzipped), UMD (0.99 kB / 0.51 kB gzipped)

Testing

The library includes 127 comprehensive tests with 100% code coverage:

# Run all tests
npm test

# Run with coverage
npm run test:coverage

# Watch mode
npm test -- --watch

Test categories:

  • Basic functionality
  • Type matching (strings, numbers, booleans, objects, arrays, etc.)
  • All API methods (on, onAny, otherwise, default, valueOf)
  • Error handling
  • Type safety
  • Real-world examples
  • Edge cases and performance

Browser Support

Works in all modern browsers and Node.js 14+:

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Node.js 14+

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

Development

# Install dependencies
npm install

# Run tests
npm test

# Build
npm run build

# Lint and format
npm run lint-format

License

MIT - See LICENSE for details

Author

Anil Kumar Thakur

Changelog

See CHANGELOG.md for release history and updates.

Keywords

match

FAQs

Package last updated on 18 Jan 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts