Match
The effect/match module provides a type-safe pattern matching system for TypeScript. Inspired by functional programming, it simplifies conditional logic by replacing verbose if/else or switch statements with a structured and expressive API.
This module supports matching against types, values, and discriminated unions while enforcing exhaustiveness checking to ensure all cases are handled.
Although pattern matching is not yet a native JavaScript feature, effect/match offers a reliable implementation that is available today.
How Pattern Matching Works
Pattern matching follows a structured process:
- Creating a matcher: Define a Matcher that operates on either a specific Match.type or Match.value.
- Defining patterns: Use combinators such as Match.when, Match.not, and Match.tag to specify matching conditions.
- Completing the match: Apply a finalizer such as Match.exhaustive, Match.orElse, or Match.option to determine how unmatched cases should be handled.
Completion
Signature
declare const either: <I, F, R, A, Pr, Ret>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => [Pr] extends [never] ? (input: I) => Either.Either<Unify<A>, R> : Either.Either<Unify<A>, R>;Example
import { Match } from "effect"
type User = { readonly role: "admin" | "editor" | "viewer" }
// Create a matcher to extract user roles
const getRole = Match.type<User>().pipe(
Match.when({ role: "admin" }, () => "Has full access"),
Match.when({ role: "editor" }, () => "Can edit content"),
Match.either, // Wrap the result in an Either
)
console.log(getRole({ role: "admin" }))
// Output: { _id: 'Either', _tag: 'Right', right: 'Has full access' }
console.log(getRole({ role: "viewer" }))
// Output: { _id: 'Either', _tag: 'Left', left: { role: 'viewer' } }exhaustive
The Match.exhaustive method finalizes the pattern matching process by ensuring that all possible cases are accounted for. If any case is missing, TypeScript will produce a type error. This is particularly useful when working with unions, as it helps prevent unintended gaps in pattern matching.
Signature
declare const exhaustive: <I, F, A, Pr, Ret>(
self: Matcher<I, F, never, A, Pr, Ret>,
) => [Pr] extends [never] ? (u: I) => Unify<A> : Unify<A>;Example
(Ensuring All Cases Are Covered)
import { Match } from "effect"
// Create a matcher for string or number values
const match = Match.type<string | number>().pipe(
// Match when the value is a number
Match.when(Match.number, (n) => `number: ${n}`),
// Mark the match as exhaustive, ensuring all cases are handled
// TypeScript will throw an error if any case is missing
// @ts-expect-error Type 'string' is not assignable to type 'never'
Match.exhaustive,
)Wraps the match result in an Option, representing an optional match.
Details
This function ensures that the result of a matcher is wrapped in an Option, making it easy to handle cases where no pattern matches. If a match is found, it returns Some(value), otherwise, it returns None.
This is useful in cases where a missing match is expected and should be handled explicitly rather than throwing an error or returning a default value.
Signature
declare const option: <I, F, R, A, Pr, Ret>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => [Pr] extends [never] ? (input: I) => Option.Option<Unify<A>> : Option.Option<Unify<A>>;Example
import { Match } from "effect"
type User = { readonly role: "admin" | "editor" | "viewer" }
// Create a matcher to extract user roles
const getRole = Match.type<User>().pipe(
Match.when({ role: "admin" }, () => "Has full access"),
Match.when({ role: "editor" }, () => "Can edit content"),
Match.option, // Wrap the result in an Option
)
console.log(getRole({ role: "admin" }))
// Output: { _id: 'Option', _tag: 'Some', value: 'Has full access' }
console.log(getRole({ role: "viewer" }))
// Output: { _id: 'Option', _tag: 'None' }Provides a fallback value when no patterns match.
Details
This function ensures that a matcher always returns a valid result, even if no defined patterns match. It acts as a default case, similar to the default clause in a switch statement or the final else in an if-else chain.
Signature
declare const orElse: <RA, Ret, F extends (_: RA) => Ret>(
f: F,
) => <I, R, A, Pr>(
self: Matcher<I, R, RA, A, Pr, Ret>,
) => [Pr] extends [never] ? (input: I) => Unify<ReturnType<F> | A> : Unify<ReturnType<F> | A>;Example
(Providing a Default Value When No Patterns Match)
import { Match } from "effect"
// Create a matcher for string or number values
const match = Match.type<string | number>().pipe(
// Match when the value is "a"
Match.when("a", () => "ok"),
// Fallback when no patterns match
Match.orElse(() => "fallback"),
)
console.log(match("a"))
// Output: "ok"
console.log(match("b"))
// Output: "fallback"orElseAbsurd
Throws an error if no pattern matches.
Details
This function finalizes a matcher by ensuring that if no patterns match, an error is thrown. It is useful when all cases should be covered, and any unexpected input should trigger an error instead of returning a default value.
When used, this function removes the need for an explicit fallback case and ensures that an unmatched value is never silently ignored.
Signature
declare const orElseAbsurd: <I, R, RA, A, Pr, Ret>(
self: Matcher<I, R, RA, A, Pr, Ret>,
) => [Pr] extends [never] ? (input: I) => Unify<A> : Unify<A>;Creating A Matcher
Creates a matcher for a specific type.
Details
This function defines a Matcher that operates on a given type, allowing you to specify conditions for handling different cases. Once the matcher is created, you can use pattern-matching functions like when to define how different values should be processed.
See
valuefor creating a matcher from a specific value.
Signature
declare const type: <I>() => Matcher<I, Types.Without<never>, I, never, never>;Example
(Matching Numbers and Strings)
import { Match } from "effect"
// Create a matcher for values that are either strings or numbers
//
// โโโโ (u: string | number) => string
// โผ
const match = Match.type<string | number>().pipe(
// Match when the value is a number
Match.when(Match.number, (n) => `number: ${n}`),
// Match when the value is a string
Match.when(Match.string, (s) => `string: ${s}`),
// Ensure all possible cases are handled
Match.exhaustive,
)
console.log(match(0))
// Output: "number: 0"
console.log(match("hello"))
// Output: "string: hello"Signature
declare const typeTags: {
<I, Ret>(): <
P extends {
[Tag in string]: (
_: Extract<
I,
{
readonly _tag: Tag;
}
>,
) => Ret;
} & { [Tag in string | number | symbol]: never },
>(
fields: P,
) => (input: I) => Ret;
<I>(): <
P extends {
[Tag in string]: (
_: Extract<
I,
{
readonly _tag: Tag;
}
>,
) => any;
} & { [Tag in string | number | symbol]: never },
>(
fields: P,
) => (input: I) => Unify<ReturnType<P[keyof P]>>;
};Creates a matcher from a specific value.
Details
This function allows you to define a Matcher directly from a given value, rather than from a type. This is useful when working with known values, enabling structured pattern matching on objects, primitives, or any data structure.
Once the matcher is created, you can use pattern-matching functions like when to define how different cases should be handled.
See
typefor creating a matcher from a specific type.
Signature
declare const value: <I>(i: I) => Matcher<I, Types.Without<never>, I, never, I>;Example
(Matching an Object by Property)
import { Match } from "effect"
const input = { name: "John", age: 30 }
// Create a matcher for the specific object
const result = Match.value(input).pipe(
// Match when the 'name' property is "John"
Match.when({ name: "John" }, (user) => `${user.name} is ${user.age} years old`),
// Provide a fallback if no match is found
Match.orElse(() => "Oh, not John"),
)
console.log(result)
// Output: "John is 30 years old"Signature
declare const valueTags: {
<
I,
P extends {
[Tag in string]: (
_: Extract<
I,
{
readonly _tag: Tag;
}
>,
) => any;
} & { [Tag in string | number | symbol]: never },
>(
fields: P,
): (input: I) => Unify<ReturnType<P[keyof P]>>;
<
I,
P extends {
[Tag in string]: (
_: Extract<
I,
{
readonly _tag: Tag;
}
>,
) => any;
} & { [Tag in string | number | symbol]: never },
>(
input: I,
fields: P,
): Unify<ReturnType<P[keyof P]>>;
};Defining Patterns
discriminator
Matches values based on a specified discriminant field.
Details
This function is used to define pattern matching on objects that follow a discriminated union structure, where a specific field (e.g., type, kind, _tag) determines the variant of the object. It allows matching multiple values of the discriminant and provides a function to handle the matched cases.
Signature
declare const discriminator: <D extends string>(
field: D,
) => <R, P extends Types.Tags<D, R> & string, Ret, Fn extends (_: Extract<R, Record<D, P>>) => Ret>(
...pattern: [first: P, values: Array<P>, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, P>>>>,
A | ReturnType<Fn>,
Pr,
Ret
>;Example
import { Match, pipe } from "effect"
const match = pipe(
Match.type<{ type: "A"; a: string } | { type: "B"; b: number } | { type: "C"; c: boolean }>(),
Match.discriminator("type")("A", "B", (_) => `A or B: ${_.type}`),
Match.discriminator("type")("C", (_) => `C(${_.c})`),
Match.exhaustive,
)discriminators
Matches values based on a field that serves as a discriminator, mapping each possible value to a corresponding handler.
Details
This function simplifies working with discriminated unions by letting you define a set of handlers for each possible value of a given field. Instead of chaining multiple calls to discriminator, this function allows defining all possible cases at once using an object where the keys are the possible values of the field, and the values are the corresponding handler functions.
Signature
declare const discriminators: <D extends string>(
field: D,
) => <
R,
Ret,
P extends { [Tag in Types.Tags<D, R> & string]: (_: Extract<R, Record<D, Tag>>) => Ret } & {
[Tag in Exclude<keyof P, Types.Tags<D, R>>]: never;
},
>(
fields: P,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, keyof P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, keyof P>>>>,
A | ReturnType<P[keyof P] & {}>,
Pr,
Ret
>;Example
import { Match, pipe } from "effect"
const match = pipe(
Match.type<{ type: "A"; a: string } | { type: "B"; b: number } | { type: "C"; c: boolean }>(),
Match.discriminators("type")({
A: (a) => a.a,
B: (b) => b.b,
C: (c) => c.c,
}),
Match.exhaustive,
)discriminatorsExhaustive
Matches values based on a discriminator field and ensures all cases are handled.
**Details*+
This function is similar to discriminators, but requires that all possible cases are explicitly handled. It is useful when working with discriminated unions, where a specific field (e.g., "type") determines the shape of an object. Each possible value of the field must have a corresponding handler, ensuring exhaustiveness checking at compile time.
This function does not require Match.exhaustive at the end of the pipeline because it enforces exhaustiveness by design.
Signature
declare const discriminatorsExhaustive: <D extends string>(
field: D,
) => <
R,
Ret,
P extends { [Tag in Types.Tags<D, R> & string]: (_: Extract<R, Record<D, Tag>>) => Ret } & {
[Tag in Exclude<keyof P, Types.Tags<D, R>>]: never;
},
>(
fields: P,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => [Pr] extends [never]
? (u: I) => Unify<A | ReturnType<P[keyof P]>>
: Unify<A | ReturnType<P[keyof P]>>;Example
import { Match, pipe } from "effect"
const match = pipe(
Match.type<{ type: "A"; a: string } | { type: "B"; b: number } | { type: "C"; c: boolean }>(),
Match.discriminatorsExhaustive("type")({
A: (a) => a.a,
B: (b) => b.b,
C: (c) => c.c,
}),
)discriminatorStartsWith
Matches values where a specified field starts with a given prefix.
Details
This function is useful for working with discriminated unions where the discriminant field follows a hierarchical or namespaced structure. It allows you to match values based on whether the specified field starts with a given prefix, making it easier to handle grouped cases.
Instead of checking for exact matches, this function lets you match values that share a common prefix. For example, if your discriminant field contains hierarchical names like "A", "A.A", and "B", you can match all values starting with "A" using a single rule.
Signature
declare const discriminatorStartsWith: <D extends string>(
field: D,
) => <R, P extends string, Ret, Fn extends (_: Extract<R, Record<D, `${P}${string}`>>) => Ret>(
pattern: P,
f: Fn,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<D, `${P}${string}`>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<D, `${P}${string}`>>>>,
A | ReturnType<Fn>,
Pr,
Ret
>;Example
import { Match, pipe } from "effect"
const match = pipe(
Match.type<{ type: "A" } | { type: "B" } | { type: "A.A" } | {}>(),
Match.discriminatorStartsWith("type")("A", (_) => 1 as const),
Match.discriminatorStartsWith("type")("B", (_) => 2 as const),
Match.orElse((_) => 3 as const),
)
console.log(match({ type: "A" })) // 1
console.log(match({ type: "B" })) // 2
console.log(match({ type: "A.A" })) // 1Excludes a specific value from matching while allowing all others.
Details
This function is useful when you need to handle all values except one or more specific cases. Instead of listing all possible matches manually, this function simplifies the logic by allowing you to specify values to exclude. Any excluded value will bypass the provided function and continue matching through other cases.
Signature
declare const not: <
R,
P extends Types.PatternPrimitive<R> | Types.PatternBase<R>,
Ret,
Fn extends (_: Types.NotMatch<R, P>) => Ret,
>(
pattern: P,
f: Fn,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddOnly<F, Types.WhenMatch<R, P>>,
Types.ApplyFilters<I, Types.AddOnly<F, Types.WhenMatch<R, P>>>,
A | ReturnType<Fn>,
Pr,
Ret
>;Example
(Ignoring a Specific Value)
import { Match } from "effect"
// Create a matcher for string or number values
const match = Match.type<string | number>().pipe(
// Match any value except "hi", returning "ok"
Match.not("hi", () => "ok"),
// Fallback case for when the value is "hi"
Match.orElse(() => "fallback"),
)
console.log(match("hello"))
// Output: "ok"
console.log(match("hi"))
// Output: "fallback"The Match.tag function allows pattern matching based on the _tag field in a [Discriminated Union](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions). You can specify multiple tags to match within a single pattern.
Note
The Match.tag function relies on the convention within the Effect ecosystem of naming the tag field as "_tag". Ensure that your discriminated unions follow this naming convention for proper functionality.
Signature
declare const tag: <
R,
P extends Types.Tags<"_tag", R> & string,
Ret,
Fn extends (_: Extract<R, Record<"_tag", P>>) => Ret,
>(
...pattern: [first: P, values: Array<P>, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<"_tag", P>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", P>>>>,
ReturnType<Fn> | A,
Pr,
Ret
>;Example
(Matching a Discriminated Union by Tag)
import { Match } from "effect"
type Event =
| { readonly _tag: "fetch" }
| { readonly _tag: "success"; readonly data: string }
| { readonly _tag: "error"; readonly error: Error }
| { readonly _tag: "cancel" }
// Create a Matcher for Either<number, string>
const match = Match.type<Event>().pipe(
// Match either "fetch" or "success"
Match.tag("fetch", "success", () => `Ok!`),
// Match "error" and extract the error message
Match.tag("error", (event) => `Error: ${event.error.message}`),
// Match "cancel"
Match.tag("cancel", () => "Cancelled"),
Match.exhaustive,
)
console.log(match({ _tag: "success", data: "Hello" }))
// Output: "Ok!"
console.log(match({ _tag: "error", error: new Error("Oops!") }))
// Output: "Error: Oops!"tagStartsWith
Matches values where the _tag field starts with a given prefix.
Details
This function allows you to match on values in a discriminated union based on whether the _tag field starts with a specified prefix. It is useful for handling hierarchical or namespaced tags, where multiple related cases share a common prefix.
Signature
declare const tagStartsWith: <
R,
P extends string,
Ret,
Fn extends (_: Extract<R, Record<"_tag", `${P}${string}`>>) => Ret,
>(
pattern: P,
f: Fn,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Extract<R, Record<"_tag", `${P}${string}`>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", `${P}${string}`>>>>,
ReturnType<Fn> | A,
Pr,
Ret
>;Example
import { Match, pipe } from "effect"
const match = pipe(
Match.type<{ _tag: "A" } | { _tag: "B" } | { _tag: "A.A" } | {}>(),
Match.tagStartsWith("A", (_) => 1 as const),
Match.tagStartsWith("B", (_) => 2 as const),
Match.orElse((_) => 3 as const),
)
console.log(match({ _tag: "A" })) // 1
console.log(match({ _tag: "B" })) // 2
console.log(match({ _tag: "A.A" })) // 1Defines a condition for matching values.
Details
This function enables pattern matching by checking whether a given value satisfies a condition. It supports both direct value comparisons and predicate functions. If the condition is met, the associated function is executed.
This function is useful when defining matchers that need to check for specific values or apply logical conditions to determine a match. It works well with structured objects and primitive types.
See
Signature
declare const when: <
R,
P extends Types.PatternPrimitive<R> | Types.PatternBase<R>,
Ret,
Fn extends (_: Types.WhenMatch<R, P>) => Ret,
>(
pattern: P,
f: Fn,
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Types.PForExclude<P>>,
Types.ApplyFilters<I, Types.AddWithout<F, Types.PForExclude<P>>>,
A | ReturnType<Fn>,
Pr,
Ret
>;Example
(Matching with Values and Predicates)
import { Match } from "effect"
// Create a matcher for objects with an "age" property
const match = Match.type<{ age: number }>().pipe(
// Match when age is greater than 18
Match.when({ age: (age) => age > 18 }, (user) => `Age: ${user.age}`),
// Match when age is exactly 18
Match.when({ age: 18 }, () => "You can vote"),
// Fallback case for all other ages
Match.orElse((user) => `${user.age} is too young`),
)
console.log(match({ age: 20 }))
// Output: "Age: 20"
console.log(match({ age: 18 }))
// Output: "You can vote"
console.log(match({ age: 4 }))
// Output: "4 is too young"Matches a value that satisfies all provided patterns.
Details
This function allows defining a condition where a value must match all the given patterns simultaneously. If the value satisfies every pattern, the associated function is executed.
Unlike when, which matches a single pattern at a time, this function ensures that multiple conditions are met before executing the callback. It is useful when checking for values that need to fulfill multiple criteria at once.
Signature
declare const whenAnd: <
R,
P extends ReadonlyArray<Types.PatternPrimitive<R> | Types.PatternBase<R>>,
Ret,
Fn extends (_: Types.WhenMatch<R, T.UnionToIntersection<P[number]>>) => Ret,
>(
...args: [patterns: P, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Types.PForExclude<T.UnionToIntersection<P[number]>>>,
Types.ApplyFilters<I, Types.AddWithout<F, Types.PForExclude<T.UnionToIntersection<P[number]>>>>,
A | ReturnType<Fn>,
Pr
>;Example
import { Match } from "effect"
type User = { readonly age: number; readonly role: "admin" | "user" }
const checkUser = Match.type<User>().pipe(
Match.whenAnd({ age: (n) => n >= 18 }, { role: "admin" }, () => "Admin access granted"),
Match.orElse(() => "Access denied"),
)
console.log(checkUser({ age: 20, role: "admin" }))
// Output: "Admin access granted"
console.log(checkUser({ age: 20, role: "user" }))
// Output: "Access denied"Matches one of multiple patterns in a single condition.
Details
This function allows defining a condition where a value matches any of the provided patterns. If a match is found, the associated function is executed. It simplifies cases where multiple patterns share the same handling logic.
Unlike when, which requires separate conditions for each pattern, this function enables combining them into a single statement, making the matcher more concise.
Signature
declare const whenOr: <
R,
P extends ReadonlyArray<Types.PatternPrimitive<R> | Types.PatternBase<R>>,
Ret,
Fn extends (_: Types.WhenMatch<R, P[number]>) => Ret,
>(
...args: [patterns: P, f: Fn]
) => <I, F, A, Pr>(
self: Matcher<I, F, R, A, Pr, Ret>,
) => Matcher<
I,
Types.AddWithout<F, Types.PForExclude<P[number]>>,
Types.ApplyFilters<I, Types.AddWithout<F, Types.PForExclude<P[number]>>>,
A | ReturnType<Fn>,
Pr,
Ret
>;Example
import { Match } from "effect"
type ErrorType =
| { readonly _tag: "NetworkError"; readonly message: string }
| { readonly _tag: "TimeoutError"; readonly duration: number }
| { readonly _tag: "ValidationError"; readonly field: string }
const handleError = Match.type<ErrorType>().pipe(
Match.whenOr({ _tag: "NetworkError" }, { _tag: "TimeoutError" }, () => "Retry the request"),
Match.when({ _tag: "ValidationError" }, (_) => `Invalid field: ${_.field}`),
Match.exhaustive,
)
console.log(handleError({ _tag: "NetworkError", message: "No connection" }))
// Output: "Retry the request"
console.log(handleError({ _tag: "ValidationError", field: "email" }))
// Output: "Invalid field: email"Model
Signature
type Case = When | Not;Pattern matching follows a structured process:
- Creating a matcher: Define a Matcher that operates on either a specific Match.type or Match.value.
- Defining patterns: Use combinators such as Match.when, Match.not, and Match.tag to specify matching conditions.
- Completing the match: Apply a finalizer such as Match.exhaustive, Match.orElse, or Match.option to determine how unmatched cases should be handled.
Signature
type Matcher<Input, Filters, RemainingApplied, Result, Provided, Return = any> =
| TypeMatcher<Input, Filters, RemainingApplied, Result, Return>
| ValueMatcher<Input, Filters, RemainingApplied, Result, Provided, Return>;Example
import { Match } from "effect"
// Simulated dynamic input that can be a string or a number
const input: string | number = "some input"
// โโโโ string
// โผ
const result = Match.value(input).pipe(
// Match if the value is a number
Match.when(Match.number, (n) => `number: ${n}`),
// Match if the value is a string
Match.when(Match.string, (s) => `string: ${s}`),
// Ensure all possible cases are covered
Match.exhaustive,
)
console.log(result)
// Output: "string: some input"Signature
interface Not {
readonly _tag: "Not";
evaluate(input: unknown): any;
guard(u: unknown): boolean;
}SafeRefinement interface
Signature
interface SafeRefinement<in A, out R = A> {
readonly [SafeRefinementId]: (a: A) => R;
}TypeMatcher interface
Signature
interface TypeMatcher<in Input, out Filters, out Remaining, out Result, out Return = any> extends Pipeable {
readonly _tag: "TypeMatcher";
readonly [MatcherTypeId]: {
readonly _filters: Covariant<Filters>;
readonly _input: Contravariant<Input>;
readonly _remaining: Covariant<Remaining>;
readonly _result: Covariant<Result>;
readonly _return: Covariant<Return>;
};
readonly cases: readonly Array<Case>;
add<I, R, RA, A>(_case: Case): TypeMatcher<I, R, RA, A>;
}ValueMatcher interface
Signature
interface ValueMatcher<
in Input,
out Filters,
out Remaining,
out Result,
out Provided,
out Return = any,
> extends Pipeable {
readonly _tag: "ValueMatcher";
readonly [MatcherTypeId]: {
readonly _filters: Covariant<Filters>;
readonly _input: Contravariant<Input>;
readonly _provided: Covariant<Result>;
readonly _remaining: Covariant<Remaining>;
readonly _result: Covariant<Result>;
readonly _return: Covariant<Return>;
};
readonly provided: Provided;
readonly value: Either<Provided, Remaining>;
add<I, R, RA, A, Pr>(_case: Case): ValueMatcher<I, R, RA, A, Pr>;
}Signature
interface When {
readonly _tag: "When";
evaluate(input: unknown): any;
guard(u: unknown): boolean;
}Other
Signature
declare const null: Predicate.Refinement<unknown, null>Signature
declare const undefined: Predicate.Refinement<unknown, undefined>;withReturnType
Ensures that all branches of a matcher return a specific type.
Details
This function enforces a consistent return type across all pattern-matching branches. By specifying a return type, TypeScript will check that every matching condition produces a value of the expected type.
Important: This function must be the first step in the matcher pipeline. If used later, TypeScript will not enforce type consistency correctly.
Signature
declare const withReturnType: <Ret>() => <I, F, R, A, Pr, _>(
self: Matcher<I, F, R, A, Pr, _>,
) => [Ret] extends [[A] extends [never] ? any : A]
? Matcher<I, F, R, A, Pr, Ret>
: "withReturnType constraint does not extend Result type";Example
(Validating Return Type Consistency)
import { Match } from "effect"
const match = Match.type<{ a: number } | { b: string }>().pipe(
// Ensure all branches return a string
Match.withReturnType<string>(),
// โ Type error: 'number' is not assignable to type 'string'
// @ts-expect-error
Match.when({ a: Match.number }, (_) => _.a),
// โ
Correct: returns a string
Match.when({ b: Match.string }, (_) => _.b),
Match.exhaustive,
)Predicates
Matches any value without restrictions.
Signature
declare const any: SafeRefinement<unknown, any>;Matches values of type bigint.
Signature
declare const bigint: Predicate.Refinement<unknown, bigint>;Matches values of type boolean.
Signature
declare const boolean: Predicate.Refinement<unknown, boolean>;Matches values that are instances of Date.
Signature
declare const date: Predicate.Refinement<unknown, Date>;Matches any defined (non-null and non-undefined) value.
Signature
declare const defined: <A>(u: A) => u is A & {};instanceOf
Matches instances of a given class.
Signature
declare const instanceOf: <A extends (...args: any) => any>(
constructor: A,
) => SafeRefinement<InstanceType<A>, never>;instanceOfUnsafe
Signature
declare const instanceOfUnsafe: <A extends (...args: any) => any>(
constructor: A,
) => SafeRefinement<InstanceType<A>, InstanceType<A>>;Matches a specific set of literal values (e.g., Match.is("a", 42, true)).
Signature
declare const is: <Literals extends ReadonlyArray<string | number | bigint | boolean | null>>(
...literals: Literals
) => SafeRefinement<Literals[number]>;nonEmptyString
Matches non-empty strings.
Signature
declare const nonEmptyString: SafeRefinement<string, never>;Matches values of type number.
Signature
declare const number: Predicate.Refinement<unknown, number>;Matches objects where keys are string or symbol and values are unknown.
Signature
declare const record: Predicate.Refinement<
unknown,
{
[x: string | symbol]: unknown;
}
>;Matches values of type string.
Signature
declare const string: Predicate.Refinement<unknown, string>;Matches values of type symbol.
Signature
declare const symbol: Predicate.Refinement<unknown, symbol>;Symbols
MatcherTypeId
Signature
declare const MatcherTypeId: unique symbol;MatcherTypeId type
Signature
type MatcherTypeId = typeof MatcherTypeId;SafeRefinementId
Signature
declare const SafeRefinementId: typeof SafeRefinementId;SafeRefinementId type
Signature
type SafeRefinementId = typeof SafeRefinementId;
Wraps the match result in an
Either, distinguishing matched and unmatched cases.Details
This function ensures that the result of a matcher is always wrapped in an
Either, allowing clear differentiation between successful matches (Right(value)) and cases where no pattern matched (Left(unmatched value)).This approach is particularly useful when handling optional values or when an unmatched case should be explicitly handled rather than returning a default value or throwing an error.