DEV Community

King Rayhan
King Rayhan

Posted on • Edited on

InlineSwitch: A Flexible Pattern Matching Library for TypeScript

Introduction

In modern JavaScript and TypeScript applications, we often need to handle complex conditional logic. Traditional switch statements and if-else chains quickly become unwieldy as complexity grows. That's why I'm excited to introduce InlineSwitch, a tiny yet powerful library that brings pattern matching to JavaScript and TypeScript.

The Problem with Traditional Approaches

Before we dive into InlineSwitch, let's examine the shortcomings of traditional conditional approaches:

Switch Statements

switch (value) {
  case 1:
    return 'One';
  case 2:
    return 'Two';
  case 3:
    return 'Three';
  default:
    return 'Unknown';
}
Enter fullscreen mode Exit fullscreen mode

Switch statements are limited to simple equality checks and require break statements to avoid fall-through issues. They also don't support complex conditions like ranges or custom logic.

If-Else Chains

if (value === 1) {
  return 'One';
} else if (value === 2) {
  return 'Two';
} else if (value === 3) {
  return 'Three';
} else {
  return 'Unknown';
}
Enter fullscreen mode Exit fullscreen mode

If-else chains are verbose and become hard to read as they grow longer. They're also difficult to maintain when adding or removing conditions.

Enter InlineSwitch

InlineSwitch is a lightweight pattern matching library that provides a clean, functional approach to conditional logic. It supports:

  1. Exact value matching
  2. Array membership testing
  3. Predicate function testing
  4. Default case handling

All with concise, readable syntax and full TypeScript support.

Installation

npm install inlineswitch
Enter fullscreen mode Exit fullscreen mode

Basic Usage

Here's a simple example of InlineSwitch in action:

import { inlineSwitch } from 'inlineswitch';

function getDiscount(userType: string) {
  return inlineSwitch(
    userType,
    [
      ['premium', () => 0.2],  // 20% discount for premium users
      ['regular', () => 0.1],  // 10% discount for regular users
      ['new', () => 0.15],     // 15% discount for new users
    ],
    () => 0.05  // 5% default discount
  );
}
Enter fullscreen mode Exit fullscreen mode

Advanced Pattern Matching

InlineSwitch really shines with more complex patterns:

Array Matching

const fruitCategory = inlineSwitch(
  'apple',
  [
    [['apple', 'pear'], () => 'pome'],
    [['peach', 'plum', 'cherry'], () => 'drupe'],
    [['grape', 'blueberry'], () => 'berry'],
  ],
  () => 'other'
);
// Result: 'pome'
Enter fullscreen mode Exit fullscreen mode

Predicate Functions

const sizeCategory = inlineSwitch(
  42,
  [
    [(n) => n < 10, () => 'small'],
    [(n) => n >= 10 && n < 100, () => 'medium'],
    [(n) => n >= 100, () => 'large'],
  ]
);
// Result: 'medium'
Enter fullscreen mode Exit fullscreen mode

Using the Value

Each handler receives the original value, allowing for more dynamic results:

const result = inlineSwitch(
  15,
  [
    [(n) => n % 3 === 0, (n) => `${n} is divisible by 3`],
    [(n) => n % 5 === 0, (n) => `${n} is divisible by 5`],
    [(n) => n % 3 === 0 && n % 5 === 0, (n) => `${n} is divisible by both 3 and 5`],
  ],
  (n) => `${n} is not divisible by 3 or 5`
);
// Result: '15 is divisible by 5'
Enter fullscreen mode Exit fullscreen mode

Type Safety

InlineSwitch is fully typed, providing excellent IntelliSense support and catching type errors at compile time:

type User = {
  id: number;
  type: 'admin' | 'editor' | 'viewer';
};

const permissions = inlineSwitch<User, string[]>(
  user,
  [
    [(u) => u.type === 'admin', () => ['read', 'write', 'delete', 'admin']],
    [(u) => u.type === 'editor', () => ['read', 'write']],
    [(u) => u.type === 'viewer', () => ['read']],
  ]
);
Enter fullscreen mode Exit fullscreen mode

Error Prevention

InlineSwitch checks for duplicate case values at runtime:

// This will throw a DuplicateCaseError
inlineSwitch(
  value,
  [
    [1, () => 'One'],
    [1, () => 'Also One'], // Duplicate key!
  ]
);
Enter fullscreen mode Exit fullscreen mode

Shorthand Syntax

For simple use cases, InlineSwitch provides a more concise API:

import { switchValue } from 'inlineswitch';

const color = switchValue(
  'red',
  ['red', () => '#ff0000'],
  ['green', () => '#00ff00'],
  ['blue', () => '#0000ff']
);
// Result: '#ff0000'
Enter fullscreen mode Exit fullscreen mode

Real-World Examples

HTTP Status Code Handling

const message = inlineSwitch(
  statusCode,
  [
    [[200, 201, 204], () => 'Success'],
    [[400, 401, 403, 404], () => 'Client Error'],
    [[500, 502, 503, 504], () => 'Server Error'],
  ],
  () => 'Unknown Status'
);
Enter fullscreen mode Exit fullscreen mode

User Roles and Permissions

const userInterface = inlineSwitch(
  userRole,
  [
    ['admin', () => <AdminDashboard />],
    ['manager', () => <ManagerView />],
    ['employee', () => <EmployeePortal />],
    ['guest', () => <GuestView />],
  ],
  () => <UnauthorizedView />
);
Enter fullscreen mode Exit fullscreen mode

Form Validation

const validationError = inlineSwitch(
  password.length,
  [
    [(len) => len < 8, () => 'Password is too short'],
    [(len) => len > 64, () => 'Password is too long'],
    [(len) => len >= 8 && len <= 64, () => null],
  ]
);
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

InlineSwitch is designed for readability and flexibility rather than raw performance. For simple equality checks where performance is critical, native switch statements might be faster. However, for complex conditional logic, InlineSwitch provides a much cleaner alternative with minimal overhead.

Under the Hood

InlineSwitch uses TypeScript's generic types and type guards to maintain type safety throughout the matching process. Here's a simplified look at the implementation:

function inlineSwitch<T, R>(
  value: T,
  cases: Array<[Pattern<T>, Handler<T, R>]>,
  defaultHandler?: Handler<T, R>
): R | undefined {
  const matchedCase = cases.find(([pattern]) => {
    if (Array.isArray(pattern)) {
      return pattern.includes(value);
    } else if (typeof pattern === 'function') {
      return pattern(value);
    } else {
      return pattern === value;
    }
  });

  return matchedCase ? matchedCase[1](value) : defaultHandler?.(value);
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

InlineSwitch brings pattern matching to JavaScript and TypeScript in a lightweight, type-safe package. By allowing you to express complex conditional logic clearly and concisely, it helps make your code more maintainable and easier to reason about.

For simple equality checks, it's as readable as a switch statement, but when you need more complex matching patterns, it scales elegantly where traditional approaches become unwieldy.

Give it a try in your next project, and enjoy the benefits of functional pattern matching in your JavaScript and TypeScript code!

npm install inlineswitch
Enter fullscreen mode Exit fullscreen mode

Resources

Happy coding!

Top comments (0)