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.
-
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.
npm install ts-switch-case
import { switchCase } from 'https://deno.land/x/ts_switch_case@v1.0.0/mod.ts';
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'
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'
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'
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'
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'
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']
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.
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.
git clone https://github.com/astralstriker/ts-switch-case.git
cd ts-switch-case
npm install
npm run build
npm test
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.
MIT License. See LICENSE for details.
File issues at GitHub Issues or contact sachdeva.jai@outlook.com.