Match.ts
Match.ts overview
Section titled “Match.ts overview”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.
Since v4.0.0
Exports Grouped by Category
Section titled “Exports Grouped by Category”- Defining patterns
- completion
- constructors
- models
- predicates
- utility types
- utils
- Types (namespace)
- Without (interface)
- Only (interface)
- WhenMatch (type alias)
- NotMatch (type alias)
- PForMatch (type alias)
- PForExclude (type alias)
- PatternBase (type alias)
- PatternPrimitive (type alias)
- AddWithout (type alias)
- AddOnly (type alias)
- ApplyFilters (type alias)
- Tags (type alias)
- ArrayToIntersection (type alias)
- ExtractMatch (type alias)
- Types (namespace)
Defining patterns
Section titled “Defining patterns”discriminator
Section titled “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.
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)See
discriminatorsfor defining several discriminator handlers at oncediscriminatorStartsWithfor 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>Since v4.0.0
discriminatorStartsWith
Section titled “discriminatorStartsWith”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".
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))
console.log(match({ type: "A" })) // 1console.log(match({ type: "B" })) // 2console.log(match({ type: "A.A" })) // 1See
discriminatorfor 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>Since v4.0.0
discriminators
Section titled “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.
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)See
discriminatorfor adding one discriminator case to a matcher pipelinediscriminatorsExhaustivefor handling every discriminator value and finalizing the matcher
Signature
declare const discriminators: <D extends string>( field: D) => < R, Ret, P extends { readonly [Tag in Types.Tags<D, R> & string]?: ((_: Extract<R, Record<D, Tag>>) => Ret) | undefined } & { readonly [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>Since v4.0.0
discriminatorsExhaustive
Section titled “discriminatorsExhaustive”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.
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 }))See
discriminatorsfor defining discriminator handlers without finalizing the matcher
Signature
declare const discriminatorsExhaustive: <D extends string>( field: D) => < R, Ret, P extends { readonly [Tag in Types.Tags<D, R> & string]: (_: Extract<R, Record<D, Tag>>) => Ret } & { readonly [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]>>Since v4.0.0
Creates 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.
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"))
console.log(match("hello"))// Output: "ok"
console.log(match("hi"))// Output: "fallback"See
whenfor adding a positive pattern case
Signature
declare const not: < R, const 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>Since v4.0.0
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.
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)
console.log(match({ _tag: "success", data: "Hello" }))// Output: "Ok!"
console.log(match({ _tag: "error", error: new Error("Oops!") }))// Output: "Error: Oops!"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>Since v4.0.0
tagStartsWith
Section titled “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.
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))
console.log(match({ _tag: "A" })) // 1console.log(match({ _tag: "B" })) // 2console.log(match({ _tag: "A.A" })) // 1Signature
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>Since v4.0.0
Matches values based on their _tag field, mapping each tag to a
corresponding handler.
Details
This function provides a way to handle discriminated unions by mapping _tag
values to specific functions. Each handler receives the matched value and
returns a transformed result. If all possible tags are handled, you can
enforce exhaustiveness using Match.exhaustive to ensure no case is missed.
Example (Mapping tag handlers)
import { Match, pipe } from "effect"
const match = pipe( Match.type< | { _tag: "A"; a: string } | { _tag: "B"; b: number } | { _tag: "C" c: boolean } >(), Match.tags({ A: (a) => a.a, B: (b) => b.b, C: (c) => c.c }), Match.exhaustive)Signature
declare const tags: < R, Ret, P extends { readonly [Tag in Types.Tags<"_tag", R> & string]?: ((_: Extract<R, Record<"_tag", Tag>>) => Ret) | undefined } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", R>>]: never }>( fields: P) => <I, F, A, Pr>( self: Matcher<I, F, R, A, Pr, Ret>) => Matcher< I, Types.AddWithout<F, Extract<R, Record<"_tag", keyof P>>>, Types.ApplyFilters<I, Types.AddWithout<F, Extract<R, Record<"_tag", keyof P>>>>, A | ReturnType<P[keyof P] & {}>, Pr, Ret>Since v4.0.0
tagsExhaustive
Section titled “tagsExhaustive”Matches values based on their _tag field and requires handling of all
possible cases.
Details
This function is designed for discriminated unions where every possible
_tag value must have a corresponding handler. Unlike tags, this
function ensures exhaustiveness, meaning all cases must be explicitly
handled. If a _tag value is missing from the mapping, TypeScript will
report an error.
Example (Handling all tag cases)
import { Match, pipe } from "effect"
const match = pipe( Match.type< | { _tag: "A"; a: string } | { _tag: "B"; b: number } | { _tag: "C" c: boolean } >(), Match.tagsExhaustive({ A: (a) => a.a, B: (b) => b.b, C: (c) => c.c }))Signature
declare const tagsExhaustive: < R, Ret, P extends { readonly [Tag in Types.Tags<"_tag", R> & string]: (_: Extract<R, Record<"_tag", Tag>>) => Ret } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", 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]>>Since v4.0.0
Defines 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.
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`))
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"See
whenOrfor handling any one of several patterns with the same handlerwhenAndfor requiring all provided patterns to match before running a handlernotfor handling inputs that do not match a patternorElsefor providing a fallback when no pattern case matches
Signature
declare const when: < R, const 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>Since v4.0.0
whenAnd
Section titled “whenAnd”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.
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"))
console.log(checkUser({ age: 20, role: "admin" }))// Output: "Admin access granted"
console.log(checkUser({ age: 20, role: "user" }))// Output: "Access denied"Signature
declare const whenAnd: < R, const 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>Since v4.0.0
whenOr
Section titled “whenOr”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.
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)
console.log(handleError({ _tag: "NetworkError", message: "No connection" }))// Output: "Retry the request"
console.log(handleError({ _tag: "ValidationError", field: "email" }))// Output: "Invalid field: email"Signature
declare const whenOr: < R, const 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>Since v4.0.0
completion
Section titled “completion”exhaustive
Section titled “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.
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)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>Since v4.0.0
option
Section titled “option”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.
Example (Extracting a user role with Match.option)
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)
console.log(getRole({ role: "admin" }))// Output: { _id: 'Option', _tag: 'Some', value: 'Has full access' }
console.log(getRole({ role: "viewer" }))// Output: { _id: 'Option', _tag: 'None' }See
resultfor preserving unmatched input as aResultfailureorElsefor replacing unmatched input with a fallback 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>>Since v4.0.0
orElse
Section titled “orElse”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.
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"))
console.log(match("a"))// Output: "ok"
console.log(match("b"))// Output: "fallback"See
optionfor finalizing unmatched input asOption.noneresultfor returning unmatched input as aResultfailureorElseAbsurdfor finalizing when unmatched input should be impossible
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>Since v4.0.0
orElseAbsurd
Section titled “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.
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)
console.log(strictMatcher("a")) // "Found A"console.log(strictMatcher("b")) // "Found B"
// This would throw an error at runtime:// strictMatcher("c" as any) // throwsSee
exhaustivefor compile-time exhaustive matcher finalizationorElsefor providing a fallback for unmatched input
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>Since v4.0.0
result
Section titled “result”Wraps 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.
Example (Extracting a user role with Match.result)
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)
console.log(getRole({ role: "admin" }))// Output: { _id: 'Result', _tag: 'Ok', ok: 'Has full access' }
console.log(getRole({ role: "viewer" }))// Output: { _id: 'Result', _tag: 'Err', err: { role: 'viewer' } }Signature
declare const result: <I, F, R, A, Pr, Ret>( self: Matcher<I, F, R, A, Pr, Ret>) => [Pr] extends [never] ? (input: I) => Result.Result<Unify<A>, R> : Result.Result<Unify<A>, R>Since v4.0.0
constructors
Section titled “constructors”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.
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"See
valuefor creating a matcher from a specific value.
Signature
declare const type: <I>() => Matcher<I, Types.Without<never>, I, never, never>Since v4.0.0
typeTags
Section titled “typeTags”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.
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..."})
console.log(formatResult({ _tag: "Success", data: "Hello World" }))// Output: "Data: Hello World"
console.log(formatResult({ _tag: "Error", message: "Network failed" }))// Output: "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" })})
console.log(processResult({ _tag: "Loading" }))// Output: { type: "pending" }Signature
declare const typeTags: { <I, Ret>(): < P extends { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract<I, { readonly _tag: Tag }>) => Ret } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", I>>]: never } >( fields: P ) => (input: I) => Ret <I>(): < P extends { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract<I, { readonly _tag: Tag }>) => any } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", I>>]: never } >( fields: P ) => (input: I) => Unify<ReturnType<P[keyof P]>>}Since v4.0.0
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.
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"))
console.log(result)// Output: "John is 30 years old"See
typefor creating a matcher from a specific type.
Signature
declare const value: <const I>(i: I) => Matcher<I, Types.Without<never>, I, never, I>Since v4.0.0
valueTags
Section titled “valueTags”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.
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}`})
console.log(message) // "Success: Hello"Signature
declare const valueTags: { < const I, P extends { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract<I, { readonly _tag: Tag }>) => any } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", I>>]: never } >( fields: P ): (input: I) => Unify<ReturnType<P[keyof P]>> < const I, P extends { readonly [Tag in Types.Tags<"_tag", I> & string]: (_: Extract<I, { readonly _tag: Tag }>) => any } & { readonly [Tag in Exclude<keyof P, Types.Tags<"_tag", I>>]: never } >( input: I, fields: P ): Unify<ReturnType<P[keyof P]>>}Since v4.0.0
models
Section titled “models”Case (type alias)
Section titled “Case (type alias)”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
Whenfor positive casesNotfor negative cases
Signature
type Case = When | NotSince v4.0.0
Matcher (type alias)
Section titled “Matcher (type alias)”Union type for matchers created by Match.type and Match.value.
Details
A Matcher carries the input type, accumulated filters, remaining cases,
result type, and, for value matchers, the provided value being matched.
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)
console.log(result)// Output: "string: some input"Signature
type Matcher<Input, Filters, RemainingApplied, Result, Provided, Return> = | TypeMatcher<Input, Filters, RemainingApplied, Result, Return> | ValueMatcher<Input, Filters, RemainingApplied, Result, Provided, Return>Since v4.0.0
Not (interface)
Section titled “Not (interface)”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.
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"))
console.log(matcher("hello")) // "Allowed: hello"console.log(matcher("forbidden")) // "This string is forbidden"Signature
export interface Not { readonly _tag: "Not" guard(u: unknown): boolean evaluate(input: unknown): any}Since v4.0.0
SafeRefinement (interface)
Section titled “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.
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"))
console.log(processValue("hello")) // "HELLO"console.log(processValue(21)) // 42console.log(processValue(true)) // "Defined: true"console.log(processValue(null)) // "Undefined or null"Signature
export interface SafeRefinement<in A, out R = A> { readonly [SafeRefinementId]: (a: A) => R}Since v4.0.0
TypeMatcher (interface)
Section titled “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.
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)
console.log(matcher("hello")) // "String: hello"console.log(matcher(42)) // "Number: 42"Signature
export interface TypeMatcher<in Input, out Filters, out Remaining, out Result, out Return = any> extends Pipeable { readonly _tag: "TypeMatcher" readonly [TypeId]: { readonly _input: T.Contravariant<Input> readonly _filters: T.Covariant<Filters> readonly _remaining: T.Covariant<Remaining> readonly _result: T.Covariant<Result> readonly _return: T.Covariant<Return> } readonly cases: ReadonlyArray<Case> add<I, R, RA, A>(_case: Case): TypeMatcher<I, R, RA, A>}Since v4.0.0
ValueMatcher (interface)
Section titled “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.
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"))
console.log(result) // "User: Alice"Signature
export interface ValueMatcher< in Input, Filters, out Remaining, out Result, Provided, out Return = any> extends Pipeable { readonly _tag: "ValueMatcher" readonly [TypeId]: { readonly _input: T.Contravariant<Input> readonly _filters: T.Covariant<Filters> readonly _result: T.Covariant<Result> readonly _return: T.Covariant<Return> } readonly provided: Provided readonly value: Result.Result<Provided, Remaining> add<I, R, RA, A, Pr>(_case: Case): ValueMatcher<I, R, RA, A, Pr>}Since v4.0.0
When (interface)
Section titled “When (interface)”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.
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)
console.log(stringMatcher("hello")) // "Got string: hello"console.log(stringMatcher(42)) // "Got number: 42"Signature
export interface When { readonly _tag: "When" guard(u: unknown): boolean evaluate(input: unknown): any}Since v4.0.0
predicates
Section titled “predicates”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.
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)
console.log(describeValue("hello"))// Output: "String: hello"
console.log(describeValue(42))// Output: "Number: 42"
console.log(describeValue([1, 2, 3]))// Output: "Other: object"
console.log(describeValue(null))// Output: "Other: object"See
definedfor matching only non-nullish valuesorElsefor providing a fallback after earlier cases
Signature
declare const any: SafeRefinement<unknown, any>Since v4.0.0
bigint
Section titled “bigint”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.
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"))
console.log(processLargeNumber(123n)) // "BigInt: 123"console.log(processLargeNumber(9007199254740992n)) // "Large integer: 9007199254740992"console.log(processLargeNumber(123)) // "Regular number: 123"console.log(processLargeNumber("123")) // "Not a numeric type"See
numberfor matching primitive number values
Signature
declare const bigint: Predicate.Refinement<unknown, bigint>Since v4.0.0
boolean
Section titled “boolean”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.
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"))
console.log(describeTruthiness(true)) // "Definitely true"console.log(describeTruthiness(false)) // "Definitely false"console.log(describeTruthiness(0)) // "Falsy number"console.log(describeTruthiness(1)) // "Some other truthy value"See
isfor matching specific literal boolean values
Signature
declare const boolean: Predicate.Refinement<unknown, boolean>Since v4.0.0
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.
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"))
console.log(processDateValue(new Date("2024-01-01"))) // "Date: 2024-01-01"console.log(processDateValue(new Date("invalid"))) // "Invalid date"console.log(processDateValue("2024-01-01")) // "Date string: 2024-01-01"console.log(processDateValue(1704067200000)) // "Not a date-related value"See
instanceOffor matching instances of any constructor
Signature
declare const date: Predicate.Refinement<unknown, Date>Since v4.0.0
defined
Section titled “defined”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.
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"))
console.log(processValue("hello"))// Output: "Defined value: hello"
console.log(processValue(42))// Output: "Defined value: 42"
console.log(processValue(0))// Output: "Defined value: 0"
console.log(processValue(""))// Output: "Defined value: "
console.log(processValue(null))// Output: "Value is null or undefined"
console.log(processValue(undefined))// Output: "Value is null or undefined"See
anyfor matching every value without excluding nullish inputs
Signature
declare const defined: <A>(u: A) => u is A & {}Since v4.0.0
instanceOf
Section titled “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.
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}`))
console.log(handleValue(new CustomError("Failed", 404))) // "Custom error: Failed (code: 404)"console.log(handleValue(new Error("Generic error"))) // "Standard error: Generic error"console.log(handleValue([1, 2, 3])) // "Array with 3 items"console.log(handleValue(new Map([["count", 1]]))) // "Map with 1 entries"See
instanceOfUnsafefor constructor matching without the same type-safety guaranteerecordfor matching broad non-null, non-array objects
Signature
declare const instanceOf: <A extends abstract new (...args: any) => any>( constructor: A) => SafeRefinement<InstanceType<A>, never>Since v4.0.0
instanceOfUnsafe
Section titled “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.
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"))See
instanceOffor type-safe constructor matching
Signature
declare const instanceOfUnsafe: <A extends abstract new (...args: any) => any>( constructor: A) => SafeRefinement<InstanceType<A>, InstanceType<A>>Since v4.0.0
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.
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}`))
console.log(handleStatus("success"))// Output: "Operation successful"
console.log(handleStatus(200))// Output: "Operation successful"
console.log(handleStatus("failed"))// Output: "Operation failed"
console.log(handleStatus(0))// Output: "Falsy value"
console.log(handleStatus("pending"))// Output: "Unknown status: pending"Signature
declare const is: <Literals extends ReadonlyArray<string | number | bigint | boolean | null>>( ...literals: Literals) => SafeRefinement<Literals[number]>Since v4.0.0
nonEmptyString
Section titled “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 (“”).
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"))
console.log(processInput("hello"))// Output: "Valid input: hello"
console.log(processInput(""))// Output: "Input cannot be empty"
console.log(processInput(" "))// Output: "Valid input: " (whitespace-only strings are considered non-empty)See
stringfor matching any string
Signature
declare const nonEmptyString: SafeRefinement<string, never>Since v4.0.0
Matches the value null.
When to use
Use to handle only the null literal in a match branch.
Details
This refinement is backed by Predicate.isNull, which checks
input === null.
See
definedfor matching non-nullish valuesisfor matching literal values
Signature
declare const null: Predicate.Refinement<unknown, null>Since v4.0.0
number
Section titled “number”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.
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"))
console.log(categorizeNumber(42)) // "Integer: 42"console.log(categorizeNumber(3.14)) // "Float: 3.14"console.log(categorizeNumber(NaN)) // "Not a number"console.log(categorizeNumber("hello")) // "Not a number type"See
bigintfor matching primitive bigint values
Signature
declare const number: Predicate.Refinement<unknown, number>Since v4.0.0
record
Section titled “record”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.
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"))
console.log(analyzeValue({ name: "Alice", age: 30 })) // "Object with 2 properties: [name, age]"console.log(analyzeValue([1, 2, 3])) // "Array with 3 items"console.log(analyzeValue(null)) // "Not an object"console.log(analyzeValue("hello")) // "Not an object"See
instanceOffor matching a specific constructor
Signature
declare const record: Predicate.Refinement< unknown, { [x: string]: unknown; [x: number]: unknown; [x: symbol]: unknown }>Since v4.0.0
string
Section titled “string”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.
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)
console.log(processValue("hello")) // "String: HELLO"console.log(processValue(42)) // "Number: 84"console.log(processValue(true)) // "Boolean: yes"Signature
declare const string: Predicate.Refinement<unknown, string>Since v4.0.0
symbol
Section titled “symbol”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.
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"))
console.log(handleSymbol(mySymbol)) // "Symbol with description: my-symbol"console.log(handleSymbol(Symbol())) // "Symbol without description"console.log(handleSymbol("string")) // "Not a symbol"Signature
declare const symbol: Predicate.Refinement<unknown, symbol>Since v4.0.0
undefined
Section titled “undefined”Matches the value undefined.
When to use
Use when a matcher should handle only inputs with no defined value.
Details
This refinement is backed by Predicate.isUndefined, which checks
input === undefined.
See
definedfor matching non-nullish valuesisfor matching literal values
Signature
declare const undefined: Predicate.Refinement<unknown, undefined>Since v4.0.0
utility types
Section titled “utility types”withReturnType
Section titled “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.
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)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"Since v4.0.0
Types (namespace)
Section titled “Types (namespace)”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.
Since v4.0.0
Without (interface)
Section titled “Without (interface)”Represents a filter that excludes specific types from a union.
Details
Without is used internally to track which types should be excluded
from consideration during pattern matching. It helps implement the
type-level logic for Match.not and other exclusion operations.
Example (Tracking excluded types)
import { Match } from "effect"
// Without is used internally when you write:Match.type<string | number | boolean>().pipe( Match.not(Match.string, (value) => `not string: ${value}`), // At this point, type system uses Without<string> to track exclusion Match.orElse(() => "was a string"))Signature
export interface Without<out X> { readonly _tag: "Without" readonly _X: X}Since v4.0.0
Only (interface)
Section titled “Only (interface)”Represents a filter that includes only specific types from a union.
Details
Only is used internally to track which types should be exclusively
considered during pattern matching. It helps implement the type-level
logic for positive matches and type narrowing.
Example (Tracking included types)
import { Match } from "effect"
// Only is used internally when you write:Match.type<string | number | boolean>().pipe( Match.when(Match.string, (s) => `string: ${s}`), // At this point, type system uses Only<string> for the match Match.orElse((value) => `not string: ${value}`))Signature
export interface Only<out X> { readonly _tag: "Only" readonly _X: X}Since v4.0.0
WhenMatch (type alias)
Section titled “WhenMatch (type alias)”Computes the matched type when a pattern P is applied to type R.
Details
This utility type determines what type a value will have after successfully matching against a pattern. It handles refinements, predicates, and complex object patterns to provide accurate type narrowing.
Example (Computing matched types)
import type { Match } from "effect"
// WhenMatch computes the narrowed type after pattern matchingtype StringMatch = Match.Types.WhenMatch<string | number, typeof Match.string>// Result: string
type ObjectMatch = Match.Types.WhenMatch< | { type: "user"; name: string } | { type: "admin" permissions: Array<string> }, { type: "user" }>// Result: { type: "user"; name: string }Signature
type WhenMatch<R, P> = [0] extends [1 & R] ? ResolvePred<P> : P extends SafeRefinement<infer SP, never> ? SP : P extends Predicate.Refinement<infer _R, infer RP> ? // try to narrow refinement [Extract<R, RP>] extends [infer X] ? [X] extends [never] ? // fallback to original refinement RP : X : never : P extends PredicateA<infer PP> ? PP : ExtractMatch<R, P>Since v4.0.0
NotMatch (type alias)
Section titled “NotMatch (type alias)”Computes the remaining type when a pattern P is excluded from type R.
Details
This utility type determines what type remains after a Match.not pattern
excludes certain values. It’s the complement of WhenMatch, calculating
what’s left after removing the matched portion.
Example (Computing unmatched types)
import type { Match } from "effect"
// NotMatch computes what remains after exclusiontype NotString = Match.Types.NotMatch<string | number | boolean, typeof Match.string>// Result: number | boolean
type NotSpecificValue = Match.Types.NotMatch<"a" | "b" | "c", "a">// Result: "b" | "c"Signature
type NotMatch<R, P> = Exclude<R, ExtractMatch<R, PForNotMatch<P>>>Since v4.0.0
PForMatch (type alias)
Section titled “PForMatch (type alias)”Resolves a pattern to its matched type for use in type computations.
Details
This utility type processes patterns (predicates, refinements, objects) and resolves them to their corresponding matched types. It’s used internally to compute type transformations during pattern matching.
Example (Resolving match patterns)
import type { Match } from "effect"
// PForMatch resolves patterns to their matched typestype StringPattern = Match.Types.PForMatch<typeof Match.string>// Result: string
type ObjectPattern = Match.Types.PForMatch<{ name: string }>// Result: { name: string }Signature
type PForMatch<P> = [ResolvePred<P>] extends [infer X] ? X : neverSince v4.0.0
PForExclude (type alias)
Section titled “PForExclude (type alias)”Computes the excluded type when a pattern P is used for exclusion.
Details
This utility type determines what should be excluded from a union type when a pattern is used in filtering operations. It transforms patterns into their exclusion-safe representations.
Example (Computing excluded patterns)
import type { Match } from "effect"
// PForExclude computes what to exclude from type operationstype ExcludeString = Match.Types.PForExclude<typeof Match.string>// Used internally to filter out string types
type ExcludeObject = Match.Types.PForExclude<{ type: "admin" }>// Used internally to filter out admin objectsSignature
type PForExclude<P> = [SafeRefinementR<ToSafeRefinement<P>>] extends [infer X] ? X : neverSince v4.0.0
PatternBase (type alias)
Section titled “PatternBase (type alias)”Defines the structure for complex object and array patterns.
Details
This type represents patterns that can match against complex data structures like objects and arrays. It supports nested pattern matching and partial object matching, enabling sophisticated pattern compositions.
Example (Describing complex object patterns)
import { Match } from "effect"
// PatternBase enables complex object patternstype UserPattern = Match.Types.PatternBase<{ name: string age: number role: "admin" | "user"}>// Allows: { name?: string | Predicate, age?: number | Predicate, ... }
// Example usage:Match.value({ name: "Alice", age: 30, role: "admin" as const }).pipe( Match.when( { age: (n: number) => n >= 18, role: "admin" }, (user: { name: string; age: number; role: "admin" }) => `Admin: ${user.name}` ), Match.orElse(() => "Not an adult admin"))Signature
type PatternBase<A> = A extends ReadonlyArray<infer _T> ? ReadonlyArray<any> | PatternPrimitive<A> : A extends Record<string, any> ? Partial<{ [K in keyof A]: PatternPrimitive<A[K] & {}> | PatternBase<A[K] & {}> }> : neverSince v4.0.0
PatternPrimitive (type alias)
Section titled “PatternPrimitive (type alias)”Defines primitive patterns that can match simple values.
Details
This type represents the building blocks of pattern matching: predicates, literal values, and safe refinements. These are the atomic patterns that can be composed into more complex matching logic.
Signature
type PatternPrimitive<A> = PredicateA<A> | A | SafeRefinement<any>Since v4.0.0
AddWithout (type alias)
Section titled “AddWithout (type alias)”Adds a type to the exclusion filter, expanding what should be filtered out.
Details
This utility type manages the accumulation of excluded types during pattern matching. When multiple exclusions are applied, it combines them into a single filter representation.
Example (Accumulating excluded types)
import { Match } from "effect"
// AddWithout is used when combining multiple exclusions:Match.type<string | number | boolean | null>().pipe( Match.not(Match.string, () => "not string"), Match.not(Match.number, () => "not number"), // Type system uses AddWithout to combine exclusions Match.orElse(() => "was string or number"))Signature
type AddWithout<A, X> = [A] extends [Without<infer WX>] ? Without<X | WX> : [A] extends [Only<infer OX>] ? Only<Exclude<OX, X>> : neverSince v4.0.0
AddOnly (type alias)
Section titled “AddOnly (type alias)”Adds a type to the inclusion filter, refining what should be included.
Details
This utility type manages the refinement of included types during pattern matching. It ensures that only the most specific type constraints are maintained when multiple positive matches are applied.
Example (Refining included types)
import { Match } from "effect"
// AddOnly is used when refining positive matches:Match.type<{ type: "user" | "admin"; name: string }>().pipe( Match.when({ type: "admin" }, (admin) => admin.name), // Type system uses AddOnly to refine the constraint Match.orElse(() => "not admin"))Signature
type AddOnly<A, X> = [A] extends [Without<infer WX>] ? [X] extends [WX] ? never : Only<X> : [A] extends [Only<infer OX>] ? [X] extends [OX] ? Only<X> : never : neverSince v4.0.0
ApplyFilters (type alias)
Section titled “ApplyFilters (type alias)”Applies accumulated filters to an input type, producing the final narrowed type.
Details
This utility type takes the collected inclusion/exclusion filters and applies them to the input type to compute the final narrowed result. It’s the culmination of the type-level filtering process.
Example (Applying accumulated filters)
import type { Match } from "effect"
// ApplyFilters computes the final narrowed type:type Result = Match.Types.ApplyFilters<string | number | boolean, Match.Types.Only<string>>// Result: string
type ExclusionResult = Match.Types.ApplyFilters<string | number | boolean, Match.Types.Without<string>>// Result: number | booleanSignature
type ApplyFilters<I, A> = A extends Only<infer X> ? X : A extends Without<infer X> ? Exclude<I, X> : neverSince v4.0.0
Tags (type alias)
Section titled “Tags (type alias)”Extracts tag values from a discriminated union based on a discriminant field.
Details
This utility type extracts the possible values of a discriminant field from a union type. It’s used internally to implement tag-based pattern matching for discriminated unions.
Example (Extracting discriminator tags)
import type { Match } from "effect"
type Events = | { _tag: "click"; x: number; y: number } | { _tag: "keypress"; key: string } | { _tag: "scroll"; delta: number }
type EventTags = Match.Types.Tags<"_tag", Events>// Result: "click" | "keypress" | "scroll"
type CustomTags = Match.Types.Tags< "type", { type: "user"; name: string } | { type: "admin"; permissions: Array<string> }>// Result: "user" | "admin"Signature
type Tags<D, P> = P extends Record<D, infer X> ? X : neverSince v4.0.0
ArrayToIntersection (type alias)
Section titled “ArrayToIntersection (type alias)”Converts an array type to an intersection of its element types.
Details
This utility type takes an array of types and converts them into a single
intersection type. It’s used internally when multiple patterns need to
be satisfied simultaneously (like in Match.whenAnd).
Example (Converting arrays to intersections)
import type { Match } from "effect"
type Combined = Match.Types.ArrayToIntersection<[{ name: string }, { age: number }, { active: boolean }]>// Result: { name: string } & { age: number } & { active: boolean }// = { name: string; age: number; active: boolean }
// This type utility enables complex type intersections// Complex type operations are handled by this utility type// for advanced pattern matching scenariosSignature
type ArrayToIntersection<A> = T.UnionToIntersection<A[number]>Since v4.0.0
ExtractMatch (type alias)
Section titled “ExtractMatch (type alias)”Extracts and narrows the matched type from an input type given a pattern.
Details
This is the core type utility that performs the actual type extraction and narrowing logic. It handles the complex type-level computation that determines what type results from applying a pattern to an input type.
Example (Extracting matched types)
import { Match } from "effect"
type StringExtract = Match.Types.ExtractMatch<string | number | boolean, typeof Match.string>// Result: string
type ObjectExtract = Match.Types.ExtractMatch< { type: "user"; name: string } | { type: "admin"; role: string }, { type: "user" }>// Result: { type: "user"; name: string }
// This powers the type narrowing in:Match.when(Match.string, (s) => s.toUpperCase())// ^^^ s is correctly typed as stringSignature
type ExtractMatch<I, P> = [ExtractAndNarrow<I, P>] extends [infer EI] ? EI : neverSince v4.0.0