Sometimes we need to judge something with multiple cases, but the main job should be done later. So we have Defered Branch!
npm install defered-branchA lightweight library providing a if-else like branch, but with deferred execution. You can evaluate conditions now and execute the matched branch later.
For more awesome packages, check out my homepageπ
- π Features
- β²οΈ Change Log
- π¦ Installation
- π Overview
- π Usage Examples
- π§ API Reference
- π§Ύ Type Annotation
- π License
- π€ Contributing
- π¦ Trivia
- π― Four Execution Patterns: Single/all-match Γ immediate/dynamic logic
- π Type Safe: Full TypeScript support with type inference
- πͺΆ Lightweight: Zero dependencies, minimal footprint
- π§© Flexible: Works with any function signature
- π¦ Generic: Customizable branch, condition, and nomatch handler types
- 2.2.1-2.2.5
- Fix: Clear matched branch after execution to prevent stale state issues.
- Add index to this document.
- Fixing anchors for npm many times π’
- 2.2.0
- Add deferedBranchAll and deferedBranchAllDynamic for multi-match scenarios.
- Fix documentation typos.
``bash`
npm install defered-branchor
pnpm add defered-branch
Evaluate conditions once, execute first match immediately.
Use cases: One-time conditional flows, static condition evaluation
Reusable branching logic, execute first match dynamically.
Use cases: Event handlers with varying states, repeated conditional logic
Evaluate conditions once, execute all matches immediately.
Use cases: Notification systems, validation rules, multiple event listeners
Reusable branching logic, execute all matches dynamically.
Use cases: Plugin systems, middleware chains, observer patterns
`typescript
const calculator = deferedBranch<(a: number, b: number) => number>();
calculator
.add(operation === 'add', (a, b) => a + b)
.add(operation === 'multiply', (a, b) => a * b)
.add(operation === 'divide', (a, b) => a / b)
.nomatch(() => {
throw new Error('Unknown operation');
});
const result = calculator.run(10, 5); // Returns calculated result
`
`typescript
import { deferedBranchDynamic } from 'defered-branch';
// Steps to use:
// 1. Create instance
const statusHandler = deferedBranchDynamic<(message: string) => void>();
// 2. Add predicate-based branches
statusHandler
.add(
() => server.status === 'online',
(msg) => logger.info(β
${msg})β οΈ ${msg}
)
.add(
() => server.status === 'maintenance',
(msg) => logger.warn()β ${msg}
)
.add(
() => server.status === 'offline',
(msg) => logger.error()
);
// 3. Add fallback handler
statusHandler.nomatch((msg) => logger.debug(π€ Unknown status: ${msg}));
// 4. Use repeatedly with different conditions
statusHandler.predicate(); // Evaluates current server.status
statusHandler.run('Server health check completed');
// Later, when server.status changes...
statusHandler.predicate(); // Re-evaluates with new status
statusHandler.run('Status updated');
`
_Same as deferedBranch_
_Same as deferedBranchDynamic_
#### Theme Switching
`typescript
const themeHandler = deferedBranchDynamic<() => void>();
themeHandler
.add(
() => preferences.theme === 'dark',
() => applyDarkTheme()
)
.add(
() => preferences.theme === 'light',
() => applyLightTheme()
)
.add(
() => preferences.theme === 'auto',
() => applySystemTheme()
)
.nomatch(() => {
// usually handles parameter validations
throw new TypeError('Invalid theme preference');
});
// Reuse whenever preferences change
function onConfigChange() {
themeHandler.predicate();
xxxHandler.predicate();
yyyHandler.predicate();
// some other logic...create some objects..
// some other logic...run some funtions...
themeHandler.run();
xxxHandler.run();
yyyHandler.run();
}
`
#### h function
This is a simplied example from package KT.js
In this section, we can see that element creation separates judgement and dealing of attr and content. With out this package, we should judge 2 times and the code would be too long for one .ts file.
`typescript
const attrBranch = deferedBranchDynamic
.add((_, attr) => typeof attr === 'string', attrIsString)
.add((_, attr) => typeof attr === 'object' && attr !== null, attrIsObject)
.nomatch(invalidAttrHandler);
const contentBranch = __Same_As_AttrBranch__;
function h
tag: T,
attr: RawAttribute = '',
content: RawContent = ''
): HTMLElement
if (typeof tag !== 'string') {
throw new TypeError('[__NAME__:h] tagName must be a string.');
}
attrBranch.predicate(null, attr);
contentBranch.predicate(null, content);
// create element
const el = document.createElement(tag) as HTMLElement
// ... some element enhancement logic ...
attrBranch.run(element, attr);
contentBranch.run(element, content);
// ... other logic ...
}
`
Creates a new deferred branch instance for immediate execution scenarios.
#### Methods
##### .add(condition: boolean, branch: Function): DeferBranch
- Adds a conditional branch that executes if condition is true
- First-match wins: the first truthy branch will be selected; later entries are ignored
- Returns the instance for chaining
##### .nomatch(handler: Function): DeferBranch
- Sets an immediate fallback handler
- Executes instantly if no previous branch matched
- Ignores later matched branches
##### .run(...args): any
- Executes the matched branch or fallback handler
- Passes arguments to the executed function
- Returns the function's return value or undefined
Creates a new dynamic deferred branch instance for reusable logic patterns.
#### Methods
##### .add(condition: Predicate, branch: Function): DeferBranchDynamic
- Adds a predicate-based conditional branch
- condition is a function that returns boolean
- First-match wins: the first truthy branch will be selected; later entries are ignored
##### .nomatch(handler: Function): DeferBranchDynamic
- Sets a fallback handler for when no predicates match
- Executes during .predicate() evaluation
##### .predicate(...args): void
- Evaluates all conditions to find the first match
- Call this before .run() to update the matched branch
- Essential for dynamic behavior
##### .run(...args): any
- Executes the currently matched branch
- Returns the function's return value or undefined
_Same as deferedBranch_
_Same as deferedBranchDynamic_
The factory functions deferedBranch, deferedBranchDynamic, deferedBranchAll, and deferedBranchAllDynamic are generic helpers. You can provide explicit type parameters to describe the branch function signature and the optional nomatch handler.
Below is an example using deferedBranch:
`typescript
import { deferedBranch } from 'defered-branch';
// Here we declare that branches accept a message (string) and return void.
// The factory signature is: deferedBranch
const handler = deferedBranch<(msg: string) => void, () => void>();
handler
.add(
process.env.STATUS === 'ok',
(msg) => console.log('OK:', msg) // this is restricted by (msg: string) => void(msg: string) => void
)
.add(
process.env.STATUS === 'warn',
(msg) => console.warn('WARN:', msg) // this is restricted by () => void
)
.nomatch(
() => console.debug('unknown') // this is restricted by
);
handler.run('hello world'); // input args for run are restricted by (msg: string) => void`
Notes:
- If you omit generic parameters TypeScript will fall back to AnyFn and inference is weaker.BranchFn
- defines the parameter types that .run(...) accepts and what each branch receives.NoMatchFn
- defines the signature of the nomatch/deferedNomatch handler.
Example: calculator with typed branches
`typescript
const calc = deferedBranchDynamic<(a: number, b: number) => number, () => never>();
calc
.add(
() => operation === 'add',
(a, b) => a + b
)
.add(
() => operation === 'mul',
(a, b) => a * b
)
.nomatch(() => {
throw new Error('unknown operation');
});
calc.predicate();
const result = calc.run(2, 3); // typed as number | undefined
`
MIT License - see LICENSE file for details.
Contributions are welcome! Please feel free to submit issues and pull requests.
KT.js is the reason of this package's birth. It uses deferedBranchDynamic internally to handle attribute and content processing in a clean, reusable way.
---
Made with β€οΈ by Kasukabe Tsumugi (´q⒠ᡠβ’q) β‘