Skip to content

Match.ts

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



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

  • 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
>

Source

Since v4.0.0

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" })) // 1
console.log(match({ type: "B" })) // 2
console.log(match({ type: "A.A" })) // 1

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
>

Source

Since v4.0.0

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

  • 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 { 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
>

Source

Since v4.0.0

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

  • discriminators for 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]>>

Source

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 values
const match = Match.type<string | number>().pipe(
// Match any value except "hi", returning "ok"
Match.not("hi", () => "ok"),
// Fallback case for when the value is "hi"
Match.orElse(() => "fallback")
)
console.log(match("hello"))
// Output: "ok"
console.log(match("hi"))
// Output: "fallback"

See

  • when for 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
>

Source

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
>

Source

Since v4.0.0

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" })) // 1
console.log(match({ _tag: "B" })) // 2
console.log(match({ _tag: "A.A" })) // 1

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
>

Source

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
>

Source

Since v4.0.0

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]>>

Source

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" property
const 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

  • whenOr for handling any one of several patterns with the same handler
  • whenAnd for requiring all provided patterns to match before running a handler
  • not for handling inputs that do not match a pattern
  • orElse for 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
>

Source

Since v4.0.0

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
>

Source

Since v4.0.0

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
>

Source

Since v4.0.0

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 values
const match = Match.type<string | number>().pipe(
// Match when the value is a number
Match.when(Match.number, (n) => `number: ${n}`),
// Mark the match as exhaustive, ensuring all cases are handled
// TypeScript will throw an error if any case is missing
// @ts-expect-error Type 'string' is not assignable to type 'never'
Match.exhaustive
)

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>

Source

Since v4.0.0

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 roles
const getRole = Match.type<User>().pipe(
Match.when({ role: "admin" }, () => "Has full access"),
Match.when({ role: "editor" }, () => "Can edit content"),
Match.option // Wrap the result in an Option
)
console.log(getRole({ role: "admin" }))
// Output: { _id: 'Option', _tag: 'Some', value: 'Has full access' }
console.log(getRole({ role: "viewer" }))
// Output: { _id: 'Option', _tag: 'None' }

See

  • result for preserving unmatched input as a Result failure
  • orElse for 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>>

Source

Since v4.0.0

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 values
const match = Match.type<string | number>().pipe(
// Match when the value is "a"
Match.when("a", () => "ok"),
// Fallback when no patterns match
Match.orElse(() => "fallback")
)
console.log(match("a"))
// Output: "ok"
console.log(match("b"))
// Output: "fallback"

See

  • option for finalizing unmatched input as Option.none
  • result for returning unmatched input as a Result failure
  • orElseAbsurd for 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>

Source

Since v4.0.0

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) // throws

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>(
self: Matcher<I, R, RA, A, Pr, Ret>
) => [Pr] extends [never] ? (input: I) => Unify<A> : Unify<A>

Source

Since v4.0.0

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 roles
const 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>

Source

Since v4.0.0

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

  • value for creating a matcher from a specific value.

Signature

declare const type: <I>() => Matcher<I, Types.Without<never>, I, never, never>

Source

Since v4.0.0

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 type
const 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 type
const 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]>>
}

Source

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 object
const result = Match.value(input).pipe(
// Match when the 'name' property is "John"
Match.when({ name: "John" }, (user) => `${user.name} is ${user.age} years old`),
// Provide a fallback if no match is found
Match.orElse(() => "Oh, not John")
)
console.log(result)
// Output: "John is 30 years old"

See

  • type for creating a matcher from a specific type.

Signature

declare const value: <const I>(i: I) => Matcher<I, Types.Without<never>, I, never, I>

Source

Since v4.0.0

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 usage
const 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]>>
}

Source

Since v4.0.0

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

  • When for positive cases
  • Not for negative cases

Signature

type Case = When | Not

Source

Since v4.0.0

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 number
const input: string | number = "some input"
// ┌─── string
// ▼
const result = Match.value(input).pipe(
// Match if the value is a number
Match.when(Match.number, (n) => `number: ${n}`),
// Match if the value is a string
Match.when(Match.string, (s) => `string: ${s}`),
// Ensure all possible cases are covered
Match.exhaustive
)
console.log(result)
// Output: "string: some input"

Signature

type Matcher<Input, Filters, RemainingApplied, Result, Provided, Return> =
| TypeMatcher<Input, Filters, RemainingApplied, Result, Return>
| ValueMatcher<Input, Filters, RemainingApplied, Result, Provided, Return>

Source

Since v4.0.0

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 patterns
const 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
}

Source

Since v4.0.0

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 refinements
const 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)) // 42
console.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
}

Source

Since v4.0.0

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 | number
const 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>
}

Source

Since v4.0.0

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 input
const 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>
}

Source

Since v4.0.0

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 patterns
const 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
}

Source

Since v4.0.0

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

  • defined for matching only non-nullish values
  • orElse for providing a fallback after earlier cases

Signature

declare const any: SafeRefinement<unknown, any>

Source

Since v4.0.0

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

  • number for matching primitive number values

Signature

declare const bigint: Predicate.Refinement<unknown, bigint>

Source

Since v4.0.0

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

  • is for matching specific literal boolean values

Signature

declare const boolean: Predicate.Refinement<unknown, boolean>

Source

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

  • instanceOf for matching instances of any constructor

Signature

declare const date: Predicate.Refinement<unknown, Date>

Source

Since v4.0.0

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

  • any for matching every value without excluding nullish inputs

Signature

declare const defined: <A>(u: A) => u is A & {}

Source

Since v4.0.0

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

  • 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 abstract new (...args: any) => any>(
constructor: A
) => SafeRefinement<InstanceType<A>, never>

Source

Since v4.0.0

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 manually
const 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

  • instanceOf for type-safe constructor matching

Signature

declare const instanceOfUnsafe: <A extends abstract new (...args: any) => any>(
constructor: A
) => SafeRefinement<InstanceType<A>, InstanceType<A>>

Source

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]>

Source

Since v4.0.0

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

  • string for matching any string

Signature

declare const nonEmptyString: SafeRefinement<string, never>

Source

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

  • defined for matching non-nullish values
  • is for matching literal values

Signature

declare const null: Predicate.Refinement<unknown, null>

Source

Since v4.0.0

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

  • bigint for matching primitive bigint values

Signature

declare const number: Predicate.Refinement<unknown, number>

Source

Since v4.0.0

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

  • instanceOf for matching a specific constructor

Signature

declare const record: Predicate.Refinement<
unknown,
{ [x: string]: unknown; [x: number]: unknown; [x: symbol]: unknown }
>

Source

Since v4.0.0

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>

Source

Since v4.0.0

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>

Source

Since v4.0.0

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

  • defined for matching non-nullish values
  • is for matching literal values

Signature

declare const undefined: Predicate.Refinement<unknown, undefined>

Source

Since v4.0.0

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"

Source

Since v4.0.0

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.

Source

Since v4.0.0

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
}

Source

Since v4.0.0

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
}

Source

Since v4.0.0

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 matching
type 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>

Source

Since v4.0.0

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 exclusion
type 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>>>

Source

Since v4.0.0

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 types
type 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 : never

Source

Since v4.0.0

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 operations
type 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 objects

Signature

type PForExclude<P> = [SafeRefinementR<ToSafeRefinement<P>>] extends [infer X] ? X : never

Source

Since v4.0.0

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 patterns
type 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] & {}> }>
: never

Source

Since v4.0.0

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>

Source

Since v4.0.0

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>>
: never

Source

Since v4.0.0

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
: never

Source

Since v4.0.0

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 | boolean

Signature

type ApplyFilters<I, A> = A extends Only<infer X> ? X : A extends Without<infer X> ? Exclude<I, X> : never

Source

Since v4.0.0

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 : never

Source

Since v4.0.0

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 scenarios

Signature

type ArrayToIntersection<A> = T.UnionToIntersection<A[number]>

Source

Since v4.0.0

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 string

Signature

type ExtractMatch<I, P> = [ExtractAndNarrow<I, P>] extends [infer EI] ? EI : never

Source

Since v4.0.0