Match
Builds pattern matchers for TypeScript values.
Match lets you add ordered cases and then finish them with a result,
fallback, Option, or exhaustive check. Use Match.type to define a
reusable matcher for a type, or Match.value to match one value immediately.
Cases can match literal values, predicates, object shapes, tags, negated
patterns, and common checks such as strings, numbers, records, and class
instances.
Completion
exhaustive
Completes a matcher that handles every remaining input case.
When to use
Use to require TypeScript to reject incomplete matcher definitions before the matcher is turned into a function.
Details
If any case is still unmatched, the matcher does not type-check as exhaustive.
Signature
declare const exhaustive: <I, F, A, Pr, Ret, Args extends Array<any>>(self: Matcher<I, F, never, A, Pr, Ret, Args>) => [Pr] extends [never] ? [Args] extends [[]] ? (u: I) => Unify<A> : (...args: Args) => Unify<A> : Unify<A>Example
(Ensuring all cases are covered)
import { Match } from "effect"
// Create a matcher for string or number valuesconst 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.
When to use
Use to finalize a matcher when unmatched input is expected and should become
Option.none.
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.
See
Signature
declare const option: <I, F, R, A, Pr, Ret, Args extends Array<any>>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => [Pr] extends [never] ? [Args] extends [[]] ? (input: I) => Option.Option<Unify<A>> : (...args: Args) => 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 rolesconst 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)
getRole({ role: "admin" })._tag // => "Some"
getRole({ role: "viewer" })._tag // => "None"Provides a fallback value when no patterns match.
When to use
Use to finalize a matcher with a fallback for unmatched input.
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.
See
- option for finalizing unmatched input as
Option.none - result for returning unmatched input as a
Resultfailure - orElseAbsurd for finalizing when unmatched input should be impossible
Signature
declare const orElse: <RA, Ret, Args extends Array<any>, F extends (_: RA, ...args: Args) => Ret>(f: F) => <I, R, A, Pr>(self: Matcher<I, R, RA, A, Pr, Ret, Args>) => [Pr] extends [never] ? [Args] extends [[]] ? (input: I) => Unify<ReturnType<F> | A> : (...args: Args) => 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 valuesconst match = Match.type<string | number>().pipe( // Match when the value is "a" Match.when("a", () => "ok"), // Fallback when no patterns match Match.orElse(() => "fallback"))
match("a") // => "ok"
match("b") // => "fallback"orElseAbsurd
Returns a matcher that throws an error if no pattern matches.
When to use
Use to finalize a matcher when every remaining unmatched case should be impossible.
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.
See
- exhaustive for compile-time exhaustive matcher finalization
- orElse for providing a fallback for unmatched input
Signature
declare const orElseAbsurd: <I, R, RA, A, Pr, Ret, Args extends Array<any>>(self: Matcher<I, R, RA, A, Pr, Ret, Args>) => [Pr] extends [never] ? [Args] extends [[]] ? (input: I) => Unify<A> : (...args: Args) => Unify<A> : Unify<A>Example
(Throwing on unmatched input)
import { Match } from "effect"
const strictMatcher = Match.type<"a" | "b">().pipe( Match.when("a", () => "Found A"), Match.when("b", () => "Found B"), // Will throw if input is neither "a" nor "b" Match.orElseAbsurd)
strictMatcher("a") // => "Found A"strictMatcher("b") // => "Found B"
// This would throw an error at runtime:// strictMatcher("c" as any) // throwsWraps the match result in a Result, distinguishing matched and unmatched
cases.
Details
This function ensures that the result of a matcher is always wrapped in an
Result, allowing clear differentiation between successful matches
(Ok(value)) and cases where no pattern matched (Err(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.
Signature
declare const result: <I, F, R, A, Pr, Ret, Args extends Array<any>>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => [Pr] extends [never] ? [Args] extends [[]] ? (input: I) => Result.Result<Unify<A>, R> : (...args: Args) => Result.Result<Unify<A>, R> : Result.Result<Unify<A>, R>Example
import { Match } from "effect"
type User = { readonly role: "admin" | "editor" | "viewer" }
// Create a matcher to extract user rolesconst getRole = Match.type<User>().pipe( Match.when({ role: "admin" }, () => "Has full access"), Match.when({ role: "editor" }, () => "Can edit content"), Match.result // Wrap the result in an Result)
getRole({ role: "admin" })._tag // => "Success"
getRole({ role: "viewer" })._tag // => "Failure"Constructors
Creates a reusable matcher from a function that selects the value to match.
The compiled matcher keeps the selector's original argument list. Case handlers receive the narrowed selected value followed by those arguments.
Signature
declare const fn: <Args extends Array<any>, I>(select: (...args: Args) => I) => Matcher<I, Types.Without<never>, I, never, never, any, Args>Example
import { Match } from "effect"
const format = Match.fn((prefix: string, value: "a" | "b") => value).pipe( Match.when("a", (_value, prefix) => `${prefix}: A`), Match.when("b", (_value, prefix) => `${prefix}: B`), Match.exhaustive)
format("status", "a") // => "status: A"Creates a matcher for a specific type.
When to use
Use to build a reusable matcher function for values of a known input 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
- value for 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)
match(0) // => "number: 0"
match("hello") // => "string: hello"Creates a type-safe match function for discriminated unions based on _tag field.
Details
This function allows you to define exhaustive pattern matching for discriminated unions
by providing handlers for each possible _tag value. It ensures type safety and
can optionally enforce a specific return type across all branches.
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]>>;}Example
(Matching type tags)
import { Match } from "effect"
type Result = | { readonly _tag: "Success"; readonly data: string } | { readonly _tag: "Error"; readonly message: string } | { readonly _tag: "Loading" }
// Create a matcher with specific return typeconst formatResult = Match.typeTags<Result, string>()({ Success: (result) => `Data: ${result.data}`, Error: (result) => `Error: ${result.message}`, Loading: () => "Loading..."})
formatResult({ _tag: "Success", data: "Hello World" }) // => "Data: Hello World"
formatResult({ _tag: "Error", message: "Network failed" }) // => "Error: Network failed"
// Create a matcher with inferred return typeconst processResult = Match.typeTags<Result>()({ Success: (result) => ({ type: "ok", value: result.data }), Error: (result) => ({ type: "error", error: result.message }), Loading: () => ({ type: "pending" })})
processResult({ _tag: "Loading" }) // => { type: "pending" }Creates a matcher from a specific value.
When to use
Use to match one concrete input immediately.
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
- type for creating a matcher from a specific type.
Signature
declare const value: <I>(i: I) => Matcher<I, Types.Without<never>, I, never, ValueFlavor>Example
(Matching an Object by Property)
import { Match } from "effect"
const input = { name: "John", age: 30 }
// Create a matcher for the specific objectconst 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"))
result // => "John is 30 years old"Creates a match function for a specific value with discriminated union handling.
Details
This function provides a convenient way to pattern match on discriminated unions
by providing an object that maps each _tag value to its corresponding handler.
It's similar to a switch statement but with better type safety and exhaustiveness checking.
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]>>;}Example
(Matching value tags)
import { Match } from "effect"
type Status = { readonly _tag: "Success"; readonly data: string }
const success: Status = { _tag: "Success", data: "Hello" }
// Simple valueTags usageconst message = Match.valueTags(success, { Success: (result) => `Success: ${result.data}`})
message // => "Success: Hello"Defining Patterns
discriminator
Matches values based on a specified discriminant field.
When to use
Use to match one or more exact values of a discriminator 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.
See
- discriminators for defining several discriminator handlers at once
- discriminatorStartsWith for matching string discriminator values by prefix
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
(Matching on a discriminator field)
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)match({ type: "A", a: "ok" }) // => "A or B: A"match({ type: "C", c: true }) // => "C(true)"discriminators
Matches values based on a field that serves as a discriminator, mapping each possible value to a corresponding handler.
When to use
Use to define several discriminator handlers at once without finalizing the matcher.
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.
See
- discriminator for adding one discriminator case to a matcher pipeline
- discriminatorsExhaustive for handling every discriminator value and finalizing the matcher
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
(Mapping discriminator handlers)
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)match({ type: "A", a: "ok" }) // => "ok"match({ type: "B", b: 42 }) // => 42discriminatorsExhaustive
Matches values by a discriminator field and requires every possible case to be handled.
When to use
Use to define an exhaustive discriminator handler map that finalizes the matcher.
Details
This is the exhaustive variant of discriminators. Each possible
discriminator value must have a corresponding handler, so the matcher is
finalized directly and does not require Match.exhaustive at the end of the
pipeline.
See
- discriminators for defining discriminator handlers without finalizing the matcher
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
(Handling all discriminator cases)
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 }))match({ type: "C", c: true }) // => truediscriminatorStartsWith
Matches values where a specified field starts with a given prefix.
When to use
Use to match string discriminator values by prefix instead of exact value.
Details
Instead of checking for exact matches, this helper matches values that share
a common prefix. For example, if the discriminant field contains hierarchical
names like "A", "A.A", and "B", a single "A" rule can match both
"A" and "A.A".
See
- discriminator for matching exact discriminator values
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
(Matching discriminator prefixes)
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))
match({ type: "A" }) // => 1match({ type: "B" }) // => 2match({ type: "A.A" }) // => 1Creates a pattern that excludes a specific value while allowing all others.
When to use
Use to add a negative pattern case for inputs that should match when another pattern does not.
Details
Any excluded value bypasses the provided function and continues matching through later cases.
See
- when for adding a positive pattern case
Signature
declare const not: <R, P extends Types.PatternPrimitive<R> | Types.PatternBase<R>, Ret, Args extends Array<any>, Fn extends (_: Types.NotMatch<R, P>, ...args: Args) => Ret>(pattern: P, f: Fn) => <I, F, A, Pr>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => Matcher<I, Types.AddOnly<F, Types.WhenMatch<R, P>>, Types.ApplyFilters<I, Types.AddOnly<F, Types.WhenMatch<R, P>>>, A | ReturnType<Fn>, Pr, Ret, Args>Example
(Ignoring a specific value)
import { Match } from "effect"
// Create a matcher for string or number valuesconst 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"))
match("hello") // => "ok"
match("hi") // => "fallback"Matches discriminated union members by their _tag field.
When to use
Use to handle one or more _tag cases with the same matcher branch.
Details
This helper follows the Effect convention that discriminated unions use
"_tag" as their discriminator field. Use discriminator for a
different discriminator field.
Signature
declare const tag: <R, P extends Types.Tags<"_tag", R> & string, Ret, Args extends Array<any>, Fn extends (_: Extract<R, Record<"_tag", P>>, ...args: Args) => Ret>(...pattern: [first: P, values: Array<P>, f: Fn]) => <I, F, A, Pr>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => 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, Args>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" }
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)
match({ _tag: "success", data: "Hello" }) // => "Ok!"
match({ _tag: "error", error: new Error("Oops!") }) // => "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
(Matching tag prefixes)
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))
match({ _tag: "A" }) // => 1match({ _tag: "B" }) // => 2match({ _tag: "A.A" }) // => 1Defines a condition for matching values.
When to use
Use to add one positive pattern case to a Match.type or Match.value
pipeline when a direct value, predicate, or structured object pattern should
run a handler for matching input.
Details
Supports both direct value comparisons and predicate functions. If the pattern matches, the associated function is executed and the matched input is removed from the remaining cases tracked by the matcher.
See
Signature
declare const when: <R, P extends Types.PatternPrimitive<R> | Types.PatternBase<R>, Ret, Args extends Array<any>, Fn extends (_: Types.WhenMatch<R, P>, ...args: Args) => Ret>(pattern: P, f: Fn) => <I, F, A, Pr>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => Matcher<I, Types.AddWithout<F, Types.PForExclude<P>>, Types.ApplyFilters<I, Types.AddWithout<F, Types.PForExclude<P>>>, A | ReturnType<Fn>, Pr, Ret, Args>Example
(Matching with values and predicates)
import { Match } from "effect"
// Create a matcher for objects with an "age" propertyconst match = Match.type<{ age: number }>().pipe( // Match when age is greater than 18 Match.when( { age: (age: number) => age > 18 }, (user: { age: number }) => `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: { age: number }) => `${user.age} is too young`))
match({ age: 20 }) // => "Age: 20"
match({ age: 18 }) // => "You can vote"
match({ age: 4 }) // => "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, Args extends Array<any>, Fn extends (_: Types.WhenMatch<R, T.UnionToIntersection<P[number]>>, ...args: Args) => Ret>(...args: [patterns: P, f: Fn]) => <I, F, A, Pr>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => 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, Ret, Args>Example
(Matching all provided patterns)
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"))
checkUser({ age: 20, role: "admin" }) // => "Admin access granted"
checkUser({ age: 20, role: "user" }) // => "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, Args extends Array<any>, Fn extends (_: Types.WhenMatch<R, P[number]>, ...args: Args) => Ret>(...args: [patterns: P, f: Fn]) => <I, F, A, Pr>(self: Matcher<I, F, R, A, Pr, Ret, Args>) => Matcher<I, Types.AddWithout<F, Types.PForExclude<P[number]>>, Types.ApplyFilters<I, Types.AddWithout<F, Types.PForExclude<P[number]>>>, A | ReturnType<Fn>, Pr, Ret, Args>Example
(Matching one of several patterns)
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)
handleError({ _tag: "NetworkError", message: "No connection" }) // => "Retry the request"
handleError({ _tag: "ValidationError", field: "email" }) // => "Invalid field: email"Guards
Matches any value without restrictions.
When to use
Use to define an explicit catch-all pattern when the handler should receive the unmatched value.
Details
This predicate matches every input, including undefined, null, objects,
primitives, and functions.
Gotchas
Match.any should usually be last because cases are checked in order and
the first matching case wins.
See
Signature
declare const any: SafeRefinement<unknown, any>Example
(Matching any remaining value)
import { Match } from "effect"
const describeValue = Match.type<unknown>() .pipe( Match.when(Match.string, (str) => `String: ${str}`), Match.when(Match.number, (num) => `Number: ${num}`), Match.when(Match.boolean, (bool) => `Boolean: ${bool}`), Match.when(Match.any, (value) => `Other: ${typeof value}`), Match.exhaustive )
describeValue("hello") // => "String: hello"
describeValue(42) // => "Number: 42"
describeValue([1, 2, 3]) // => "Other: object"
describeValue(null) // => "Other: object"Matches values of type bigint.
When to use
Use to match primitive bigint values.
Details
This predicate refines unknown values to bigints, allowing pattern matching on bigint types. BigInts are used for representing integers with arbitrary precision.
See
- number for matching primitive number values
Signature
declare const bigint: Predicate.Refinement<unknown, bigint>Example
(Matching bigint values)
import { Match } from "effect"
const processLargeNumber = Match.type<unknown>().pipe( Match.when(Match.bigint, (big) => { if (big > 9007199254740991n) { return `Large integer: ${big.toString()}` } return `BigInt: ${big.toString()}` }), Match.when(Match.number, (num) => `Regular number: ${num}`), Match.orElse(() => "Not a numeric type"))
processLargeNumber(123n) // => "BigInt: 123"processLargeNumber(9007199254740992n) // => "Large integer: 9007199254740992"processLargeNumber(123) // => "Regular number: 123"processLargeNumber("123") // => "Not a numeric type"Matches values of type boolean.
When to use
Use to match primitive boolean values.
Details
This predicate refines unknown values to booleans, allowing pattern matching
on boolean types. It only matches the primitive boolean values true and false.
See
- is for matching specific literal boolean values
Signature
declare const boolean: Predicate.Refinement<unknown, boolean>Example
(Matching boolean values)
import { Match } from "effect"
const describeTruthiness = Match.type<unknown>().pipe( Match.when( Match.boolean, (bool) => bool ? "Definitely true" : "Definitely false" ), Match.when(0, () => "Falsy number"), Match.when("", () => "Empty string"), Match.when(Match.null, () => "Null value"), Match.orElse(() => "Some other truthy value"))
describeTruthiness(true) // => "Definitely true"describeTruthiness(false) // => "Definitely false"describeTruthiness(0) // => "Falsy number"describeTruthiness(1) // => "Some other truthy value"Matches values that are instances of Date.
When to use
Use to match Date instances.
Details
This predicate refines unknown values to Date instances, allowing pattern matching on Date objects. It only matches actual Date instances, not date strings or timestamps.
See
- instanceOf for matching instances of any constructor
Signature
declare const date: Predicate.Refinement<unknown, Date>Example
(Matching Date instances)
import { Match } from "effect"
const processDateValue = Match.type<unknown>().pipe( Match.when(Match.date, (date) => { if (isNaN(date.getTime())) { return "Invalid date" } return `Date: ${date.toISOString().split("T")[0]}` }), Match.when(Match.string, (str) => `Date string: ${str}`), Match.orElse(() => "Not a date-related value"))
processDateValue(new Date("2024-01-01")) // => "Date: 2024-01-01"processDateValue(new Date("invalid")) // => "Invalid date"processDateValue("2024-01-01") // => "Date string: 2024-01-01"processDateValue(1704067200000) // => "Not a date-related value"Matches any defined (non-null and non-undefined) value.
When to use
Use to exclude only null and undefined from a match branch.
Details
This predicate matches values that are neither null nor undefined,
effectively filtering out nullish values while preserving all other types.
See
- any for matching every value without excluding nullish inputs
Signature
declare const defined: <A>(u: A) => u is A & {}Example
(Matching defined values)
import { Match } from "effect"
const processValue = Match.type<string | number | null | undefined>() .pipe( Match.when(Match.defined, (value) => `Defined value: ${value}`), Match.orElse(() => "Value is null or undefined") )
processValue("hello") // => "Defined value: hello"
processValue(42) // => "Defined value: 42"
processValue(0) // => "Defined value: 0"
processValue("") // => "Defined value: "
processValue(null) // => "Value is null or undefined"
processValue(undefined) // => "Value is null or undefined"instanceOf
Matches instances of a given class.
When to use
Use to match values that are instances of a constructor with type-safe narrowing.
Details
This predicate checks if a value is an instance of the specified constructor, providing type-safe matching for class instances and built-in objects.
See
- instanceOfUnsafe for constructor matching without the same type-safety guarantee
- record for matching broad non-null, non-array objects
Signature
declare const instanceOf: <A extends (...args: any) => any>(constructor: A) => SafeRefinement<InstanceType<A>, never>Example
(Matching class instances)
import { Match } from "effect"
class CustomError extends Error { constructor(message: string, public code: number) { super(message) }}
const handleValue = Match.type<unknown>() .pipe( Match.when( Match.instanceOf(CustomError), (err) => `Custom error: ${err.message} (code: ${err.code})` ), Match.when( Match.instanceOf(Error), (err) => `Standard error: ${err.message}` ), Match.when( Match.instanceOf(Array), (arr) => `Array with ${arr.length} items` ), Match.when( Match.instanceOf(Map), (map) => `Map with ${map.size} entries` ), Match.orElse((value) => `Other: ${typeof value}`) )
handleValue(new CustomError("Failed", 404)) // => "Custom error: Failed (code: 404)"handleValue(new Error("Generic error")) // => "Standard error: Generic error"handleValue([1, 2, 3]) // => "Array with 3 items"handleValue(new Map([["count", 1]])) // => "Map with 1 entries"instanceOfUnsafe
Checks whether a value is an instance of a constructor without type-safe narrowing.
When to use
Use when you need constructor matching to use the unsafe refinement type.
Details
This predicate checks if a value is an instance of the specified constructor
but doesn't provide the same type safety guarantees as the regular instanceOf.
Use this when you need more flexibility but understand the type safety implications.
See
- instanceOf for type-safe constructor matching
Signature
declare const instanceOfUnsafe: <A extends (...args: any) => any>(constructor: A) => SafeRefinement<InstanceType<A>, InstanceType<A>>Example
(Matching class instances unsafely)
import { Match } from "effect"
class CustomError extends Error { constructor(message: string, public code: number) { super(message) }}
// When you need to match instances but handle type narrowing manuallyconst handleError = Match.type<unknown>().pipe( Match.when(Match.instanceOfUnsafe(CustomError), (err: any) => { // Manual type assertion needed const customErr = err as CustomError return `Custom error ${customErr.code}: ${customErr.message}` }), Match.orElse(() => "Not a CustomError"))handleError(new CustomError("failed", 500)) // => "Custom error 500: failed"Matches a specific set of literal values (e.g., Match.is("a", 42, true)).
When to use
Use to match one of several literal primitive or null values.
Details
This function creates a predicate that matches any of the provided literal values. It's useful for matching against multiple specific values in a single pattern.
Signature
declare const is: <Literals extends ReadonlyArray<string | number | bigint | boolean | null>>(...literals: Literals) => SafeRefinement<Literals[number]>Example
(Matching literal values)
import { Match } from "effect"
const handleStatus = Match.type<string | number>() .pipe( Match.when(Match.is("success", "ok", 200), () => "Operation successful"), Match.when(Match.is("error", "failed", 500), () => "Operation failed"), Match.when(Match.is(0, false, null), () => "Falsy value"), Match.orElse((value) => `Unknown status: ${value}`) )
handleStatus("success") // => "Operation successful"
handleStatus(200) // => "Operation successful"
handleStatus("failed") // => "Operation failed"
handleStatus(0) // => "Falsy value"
handleStatus("pending") // => "Unknown status: pending"nonEmptyString
Matches non-empty strings.
When to use
Use to match strings whose length is greater than zero.
Details
This predicate matches any string that contains at least one character, effectively filtering out empty strings ("").
See
- string for matching any string
Signature
declare const nonEmptyString: SafeRefinement<string, never>Example
(Matching non-empty strings)
import { Match } from "effect"
const processInput = Match.type<string>() .pipe( Match.when(Match.nonEmptyString, (str) => `Valid input: ${str}`), Match.orElse(() => "Input cannot be empty") )
processInput("hello") // => "Valid input: hello"
processInput("") // => "Input cannot be empty"
processInput(" ") // => "Valid input: "Matches values of type number.
When to use
Use to match primitive number values, including NaN and infinities.
Details
This predicate refines unknown values to numbers, allowing pattern matching
on numeric types. It matches all number values including integers, floats,
Infinity, -Infinity, and NaN.
See
- bigint for matching primitive bigint values
Signature
declare const number: Predicate.Refinement<unknown, number>Example
(Matching number values)
import { Match } from "effect"
const categorizeNumber = Match.type<unknown>().pipe( Match.when(Match.number, (num) => { if (Number.isNaN(num)) return "Not a number" if (!Number.isFinite(num)) return "Infinite" if (Number.isInteger(num)) return `Integer: ${num}` return `Float: ${num.toFixed(2)}` }), Match.orElse(() => "Not a number type"))
categorizeNumber(42) // => "Integer: 42"categorizeNumber(3.14) // => "Float: 3.14"categorizeNumber(NaN) // => "Not a number"categorizeNumber("hello") // => "Not a number type"Matches non-null objects other than arrays.
When to use
Use to match broad non-null, non-array object values.
Details
This predicate uses Predicate.isObject: it returns true for values whose
runtime type is "object", are not null, and are not arrays. It can match
Date, RegExp, and class instances; use instanceOf or a more specific
pattern when those cases need to be distinguished.
See
- instanceOf for matching a specific constructor
Signature
declare const record: Predicate.Refinement<unknown, { [x: string | number | symbol]: unknown;}>Example
(Matching record objects)
import { Match } from "effect"
const analyzeValue = Match.type<unknown>().pipe( Match.when(Match.record, (obj) => { const keys = Object.keys(obj) const valueCount = keys.length return `Object with ${valueCount} properties: [${keys.join(", ")}]` }), Match.when( Match.instanceOf(Array), (arr) => `Array with ${arr.length} items` ), Match.orElse(() => "Not an object"))
analyzeValue({ name: "Alice", age: 30 }) // => "Object with 2 properties: [name, age]"analyzeValue([1, 2, 3]) // => "Array with 3 items"analyzeValue(null) // => "Not an object"analyzeValue("hello") // => "Not an object"Matches values of type string.
Details
This predicate refines unknown values to strings, allowing pattern matching on string types. It's commonly used in type-based matchers to handle string cases.
Signature
declare const string: Predicate.Refinement<unknown, string>Example
(Matching string values)
import { Match } from "effect"
const processValue = Match.type<string | number | boolean>().pipe( Match.when(Match.string, (str) => `String: ${str.toUpperCase()}`), Match.when(Match.number, (num) => `Number: ${num * 2}`), Match.when(Match.boolean, (bool) => `Boolean: ${bool ? "yes" : "no"}`), Match.exhaustive)
processValue("hello") // => "String: HELLO"processValue(42) // => "Number: 84"processValue(true) // => "Boolean: yes"Matches values of type symbol.
Details
This predicate refines unknown values to symbols, allowing pattern matching on symbol types. Symbols are unique identifiers that are often used as object keys or for creating private properties.
Signature
declare const symbol: Predicate.Refinement<unknown, symbol>Example
(Matching symbol values)
import { Match } from "effect"
const mySymbol = Symbol("my-symbol")const globalSymbol = Symbol.for("global-symbol")
const handleSymbol = Match.type<unknown>().pipe( Match.when(Match.symbol, (sym) => { const description = sym.description if (description) { return `Symbol with description: ${description}` } return "Symbol without description" }), Match.orElse(() => "Not a symbol"))
handleSymbol(mySymbol) // => "Symbol with description: my-symbol"handleSymbol(Symbol()) // => "Symbol without description"handleSymbol("string") // => "Not a symbol"Models
Represents a single pattern matching case.
When to use
Use as the common public type for code that needs to inspect, store, or pass either positive or negative pattern matching cases.
Details
A Case can be either a positive match (When) or a negative match (Not).
Cases are the building blocks of pattern matching logic and determine
how values are tested and transformed.
See
Signature
type Case = When | NotUnion type for matchers created by Match.type and Match.value.
Details
A Matcher carries the input type, accumulated filters, remaining cases,
result type, and a flavor distinguishing the two matcher variants: never
for matchers created with Match.type and ValueFlavor for matchers created
with Match.value. Because the flavor never depends on the input type,
terminal combinators resolve even when the input contains type parameters.
Signature
type Matcher<Input, Filters, RemainingApplied, Result, Flavor, Return = any, Args extends Array<any> = []> = TypeMatcher<Input, Filters, RemainingApplied, Result, Return, Args> | ValueMatcher<Input, Filters, RemainingApplied, Result, Input, Return, Flavor>Example
(Matching string and number values)
import { Match } from "effect"
// Simulated dynamic input that can be a string or a numberconst 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)
result // => "string: some input"Represents a negative pattern matching case.
Details
A Not case contains the logic to test if a value does NOT match a specific
pattern and the function to evaluate when the pattern doesn't match. It's used
for exclusion-based pattern matching.
Signature
interface Not { readonly _tag: "Not"; evaluate(input: unknown, ...args: Array<any>): any; guard(u: unknown): boolean;}Example
(Creating negative match cases)
import { Match } from "effect"
// Not creates cases that exclude specific patternsconst matcher = Match.type<string>().pipe( // Match any string except "forbidden" Match.not("forbidden", (s) => `Allowed: ${s}`), Match.orElse(() => "This string is forbidden"))
matcher("hello") // => "Allowed: hello"matcher("forbidden") // => "This string is forbidden"SafeRefinement interface
A safe refinement that narrows types without runtime errors.
Details
SafeRefinement provides a way to refine types in pattern matching while
maintaining type safety. Unlike regular predicates, safe refinements can
transform the matched value's type without throwing runtime errors.
Signature
interface SafeRefinement<in A, out R = A> { readonly "~effect/match/Match/SafeRefinement": (a: A) => R;}Example
(Using safe refinements)
import { Match } from "effect"
// Built-in safe refinementsconst processValue = Match.type<unknown>().pipe( Match.when(Match.string, (s) => s.toUpperCase()), Match.when(Match.number, (n) => n * 2), Match.when(Match.defined, (value) => `Defined: ${value}`), Match.orElse(() => "Undefined or null"))
processValue("hello") // => "HELLO"processValue(21) // => 42processValue(true) // => "Defined: true"processValue(null) // => "Undefined or null"TypeMatcher interface
Represents a pattern matcher that operates on types rather than specific values.
Details
A TypeMatcher is created when using Match.type<T>() and allows you to define
patterns that will be applied to values of the specified type. It maintains
type-level information about the input type, applied filters, remaining cases,
and expected results.
Signature
interface TypeMatcher<in Input, out Filters, out Remaining, out Result, out Return = any, in Args extends Array<any> = []> extends Pipeable { readonly _tag: "TypeMatcher"; readonly "~effect/match/Match/Matcher": { readonly _args: Contravariant<Args>; 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>; readonly select: (...args: Array<any>) => unknown; add<I, R, RA, A>(_case: Case): TypeMatcher<I, R, RA, A, Return, Args>;}Example
(Creating a type matcher)
import { Match } from "effect"
// Create a TypeMatcher for string | numberconst matcher = Match.type<string | number>().pipe( Match.when(Match.string, (s) => `String: ${s}`), Match.when(Match.number, (n) => `Number: ${n}`), Match.exhaustive)
matcher("hello") // => "String: hello"matcher(42) // => "Number: 42"ValueFlavor type
Marker used by Matcher to distinguish matchers created with Match.value.
Signature
type ValueFlavor = "value"ValueMatcher interface
Represents a pattern matcher that operates on a specific provided value.
Details
A ValueMatcher is created when using Match.value(someValue) and contains
the actual value to be matched against. It tracks both the provided value
and the result of applying patterns to determine matches. Its optional
seventh type parameter is the matcher flavor and defaults to ValueFlavor.
Signature
interface ValueMatcher<in Input, Filters, out Remaining, out Result, Provided, out Return = any, out Flavor = ValueFlavor> extends Pipeable { readonly _tag: "ValueMatcher"; readonly "~effect/match/Match/Matcher": { readonly _filters: Covariant<Filters>; readonly _flavor: Covariant<Flavor>; readonly _input: Contravariant<Input>; readonly _result: Covariant<Result>; readonly _return: Covariant<Return>; }; readonly provided: Provided; readonly value: Result<Provided, Remaining>; add<I, R, RA, A, Provided>(_case: Case): ValueMatcher<I, R, RA, A, Provided>;}Example
(Creating a value matcher)
import { Match } from "effect"
const input = { type: "user", name: "Alice", age: 30 }
// Create a ValueMatcher for the specific inputconst result = Match.value(input).pipe( Match.when({ type: "user" }, (user) => `User: ${user.name}`), Match.when({ type: "admin" }, (admin) => `Admin: ${admin.name}`), Match.orElse(() => "Unknown type"))
result // => "User: Alice"Represents a positive pattern matching case.
Details
A When case contains the logic to test if a value matches a specific pattern
and the function to evaluate when the pattern matches. It's the primary
building block for pattern matching conditions.
Signature
interface When { readonly _tag: "When"; evaluate(input: unknown, ...args: Array<any>): any; guard(u: unknown): boolean;}Example
(Creating positive match cases)
import { Match } from "effect"
// When creates cases that match specific patternsconst stringMatcher = Match.type<string | number>().pipe( Match.when(Match.string, (s: string) => `Got string: ${s}`), Match.when(Match.number, (n: number) => `Got number: ${n}`), Match.exhaustive)
stringMatcher("hello") // => "Got string: hello"stringMatcher(42) // => "Got number: 42"Other
Signature
declare const null: Predicate.Refinement<unknown, null>A namespace containing utility types for Match operations.
Details
This namespace provides advanced type-level utilities used internally by the Match module to perform complex pattern matching, type narrowing, and filter application. These types enable the sophisticated type inference that makes pattern matching both type-safe and ergonomic.
Signature
declare const undefined: Predicate.Refinement<unknown, undefined>Utility Types
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, _, Args extends Array<any>>(self: Matcher<I, F, R, A, Pr, _, Args>) => [Ret] extends [[A] extends [never] ? any : A] ? Matcher<I, F, R, A, Pr, Ret, Args> : "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)