ts-switch-case
Version:
A TypeScript-first switch-case utility with object-based and chainable syntax
113 lines (110 loc) • 5.13 kB
text/typescript
/**
* Utility type to extract the variant of a discriminated union.
*
* @template T - The union type.
* @template K - The discriminator key.
* @template V - The discriminator value.
*/
type VariantOf<T, K extends keyof T, V> = T extends Record<K, V> ? T : never;
/**
* Represents a handler for a case in a switch-case structure.
*
* @template T - The type of the value being matched.
* @template R - The return type of the handler function.
* @property {T} match - A value or a function to match against the input.
* @property {(val: T) => R} handler - A function that processes the matched value and returns a result.
*/
type CaseHandler<T, R> = {
match: T;
handler: (val: T) => R;
} | {
match: (val: T) => boolean;
handler: (val: T) => R;
};
/**
* Interface for building a switch-case structure with fluent API.
*
* @template T - The type of the value being matched.
* @template R - The return type of the handler functions.
* @template Remaining - The remaining keys that can be matched.
*/
interface SwitchCaseBuilder<T, R, Remaining extends string | number | symbol = never> {
/**
* Adds a case to the switch-case structure.
*
* @template K - The type of the key being matched.
* @param {K} match - A value to match or a function to evaluate the match.
* @param {R | (() => R)} handler - A result or a function that returns a result for the matched case.
* @returns {SwitchCaseBuilder<T, R, Exclude<Remaining, K>>} - The updated builder with the new case added.
*/
case: (<K extends Remaining>(match: K, handler: R | (() => R)) => SwitchCaseBuilder<T, R, Exclude<Remaining, K>>) & ((match: (val: T) => boolean, handler: R | ((val: T) => R)) => SwitchCaseBuilder<T, R, Remaining>);
/**
* Adds a default handler for unmatched cases.
*
* @param {(val: T) => R} handler - A function to handle unmatched cases.
* @returns {SwitchCaseBuilder<T, R, never>} - The updated builder with the default handler added.
*/
default: (handler: (val: T) => R) => SwitchCaseBuilder<T, R, never>;
/**
* Ensures all cases are handled and returns the result.
*
* @returns {Remaining extends never ? R : never} - The result of the matched case or an error if cases are missing.
*/
exhaustive: () => Remaining extends never ? R : never;
/**
* Executes the switch-case structure and returns the result.
*
* @returns {R} - The result of the matched case or the default handler.
*/
run: () => R;
}
/**
* Interface for building a discriminated union switch-case structure with fluent API.
*
* @template T - The type of the value being matched.
* @template K - The discriminator key.
* @template R - The return type of the handler functions.
* @template Remaining - The remaining discriminator values that can be matched.
*/
interface DiscriminatedUnionSwitchCaseBuilder<T, K extends keyof T, R, Remaining extends string | number | symbol = string | number | symbol> {
/**
* Adds a case for a specific discriminator value.
*
* @template V - The discriminator value.
* @param {V} value - The discriminator value to match.
* @param {(val: VariantOf<T, K, V>) => R} handler - Handler for this case.
* @returns {DiscriminatedUnionSwitchCaseBuilder<T, K, R, Exclude<Remaining, V>>}
*/
case: <V extends Remaining>(value: V, handler: (val: VariantOf<T, K, V>) => R) => DiscriminatedUnionSwitchCaseBuilder<T, K, R, Exclude<Remaining, V>>;
/**
* Adds a default handler for unmatched cases.
*
* @param {(val: T) => R} handler - Handler for unmatched cases.
* @returns {DiscriminatedUnionSwitchCaseBuilder<T, K, R, never>}
*/
default: (handler: (val: T) => R) => DiscriminatedUnionSwitchCaseBuilder<T, K, R, never>;
/**
* Executes the switch-case structure and returns the result.
*
* @returns {R} - The result of the matched case or the default handler.
*/
run: () => R;
}
declare function switchCase<T extends string | number | symbol, R>(value: T, cases: {
[K in T]: R | (() => R);
}, defaultHandler?: (val: T) => R): R;
declare function switchCase<T, K extends keyof T, V extends T[K] & (string | number), R>(value: T, discriminator: K, cases: {
[P in V]: R | ((val: T & {
[Q in K]: P;
}) => R);
}, defaultHandler?: (val: T) => R): R;
declare function switchCase<T, R>(value: T, cases: Record<string, CaseHandler<T, R>>, defaultHandler?: (val: T) => R): R;
declare function switchCase<T, R>(value: T, cases: Array<{
match: (val: T) => boolean;
handler: R | ((val: T) => R);
}>, defaultHandler?: (val: T) => R): R;
declare function switchCase<T extends string | number | symbol, R>(value: T): SwitchCaseBuilder<T, R, T>;
declare function switchCase<T, R>(value: T): SwitchCaseBuilder<T, R, never>;
declare function switchCase<T, K extends keyof T, R>(value: T, discriminator: K): DiscriminatedUnionSwitchCaseBuilder<T, K, R, T[K] & (string | number | symbol)>;
declare function assertUnreachable(value: never): never;
export { assertUnreachable, switchCase };