ts-switch-case
v1.0.5
Published
A TypeScript-first switch-case utility with object-based and chainable syntax
Maintainers
Readme
ts-switch-case
A TypeScript-first control flow utility for modern JavaScript applications, offering a powerful, type-safe alternative to native switch statements. With dual syntax (object-based and chainable), ts-switch-case supports plain values (strings, numbers, symbols), pattern matching, boolean conditions (like switch (true)), and discriminated unions, making it ideal for web, serverless, and API-driven projects.
Inspired by Kotlin's when and Rust's match, ts-switch-case combines conciseness, type safety, and flexibility, outperforming npm alternatives like ts-pattern and switch with its intuitive API and lightweight footprint.
Features
- Dual Syntax: Choose between object-based (
{ 200: 'OK' }) for static mappings or chainable (.case(200, 'OK')) for fluent dynamic logic. - Plain Values: Use simple values (e.g.,
'OK') or functions for handlers, supporting strings, numbers, and symbols. - Pattern Matching: Support predicates (e.g.,
p => p > 10) and boolean conditions (switch (true)-style). - Discriminated Unions: Type-safe handling of tagged unions (e.g., API responses).
- Type Safety: Enforces exhaustiveness and narrows types in handlers.
- Lightweight: No dependencies, optimized for Next.js Edge Runtime, Deno, and AWS Lambda.
- TypeScript-First: Built with advanced TypeScript for compile-time guarantees.
Installation
npm
npm install ts-switch-caseDeno
import { switchCase } from 'https://deno.land/x/[email protected]/mod.ts';Usage
Literal Types (Object-based)
Map HTTP status codes to messages with concise syntax:
import { switchCase } from 'ts-switch-case';
type HttpStatus = 200 | 404 | 500;
const code = 404 as HttpStatus;
const message = switchCase(code, {
200: 'OK',
404: 'Not Found',
500: 'Server Error',
}); // message = 'Not Found'Literal Types (Chainable)
Use fluent syntax for the same mapping:
const message = switchCase(code)
.case(200, 'OK')
.case(404, 'Not Found')
.case(500, 'Server Error')
.exhaustive(); // message = 'Not Found'Discriminated Unions
Handle API response types with type-safe narrowing:
type ApiResponse =
| { type: 'success'; data: string }
| { type: 'error'; message: string }
| { type: 'loading' };
const response = { type: 'success', data: 'User created' } as ApiResponse;
const result = switchCase(response, 'type', {
success: ({ data }) => `Success: ${data}`,
error: ({ message }) => `Error: ${message}`,
loading: () => 'Loading...',
}); // result = 'Success: User created'Predicates
Match scores with custom conditions:
const score: number = 85;
const grade = switchCase(
score,
{
excellent: { match: (s) => s >= 90, handler: 'A' },
good: { match: (s) => s >= 80, handler: 'B' },
average: { match: (s) => s >= 70, handler: 'C' },
},
'F'
); // grade = 'B'Boolean Conditions (Chainable, switch (true))
Mimic switch (true) with sequential predicates:
const age: number = 25;
const category = switchCase(age)
.case(a => a < 13, 'Child')
.case(a => a < 20, 'Teen')
.case(a => a >= 20, 'Adult')
.default(() => 'Unknown')
.run(); // category = 'Adult'User Roles
Map user roles to permissions:
type Role = 'admin' | 'editor' | 'viewer';
const role = 'editor' as Role;
const permissions = switchCase(role, {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read'],
}); // permissions = ['read', 'write']Why ts-switch-case?
Compared to alternatives:
- vs.
ts-pattern: More concise object-based syntax for literals and discriminated unions; dual syntax offers flexibility. - vs.
switch: Adds boolean conditions, discriminated unions, symbols, and modern TypeScript. - vs.
switch-case: Supports plain values, type safety, and chainable API. - vs. Native
switch: Returns values, enforces exhaustiveness, and supports advanced matching.
Handling Cyclic References
ts-switch-case includes cycle detection via isCyclic and logCyclicError. If a cyclic reference is detected (e.g., in cases or results), an error is thrown with a message pointing to this section.
For React applications, cyclic references often occur in React.ReactNode (e.g., JSX elements with internal fiber properties). To handle this, you can implement a sanitizeNode function to safely process nodes. Example:
import { isValidElement } from 'react';
import { isCyclic } from 'ts-switch-case';
function sanitizeNode(node: React.ReactNode): React.ReactNode {
if (isValidElement(node)) {
const { children, ...safeProps } = node.props;
return { ...node, props: { ...safeProps, children: sanitizeNode(children) } };
}
if (isCyclic(node)) return '[Cyclic Node]';
return node;
}Use sanitizeNode in your switchCase handlers to avoid cyclic errors:
import { switchCase } from 'ts-switch-case';
const node = <div>Cyclic</div>;
const result = switchCase(node)
.case(v => typeof v === 'string', v => v)
.default(v => sanitizeNode(v))
.run();For non-React contexts, use isCyclic to check inputs and handle cycles appropriately.
Setup for Development
git clone https://github.com/astralstriker/ts-switch-case.git
cd ts-switch-case
npm install
npm run build
npm testContributing
Contributions are welcome! Please:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/xyz). - Commit changes (
git commit -m 'Add feature xyz'). - Push to the branch (
git push origin feature/xyz). - Open a pull request.
License
MIT License. See LICENSE for details.
Support
File issues at GitHub Issues or contact [email protected].
