ts-pattern
Version:
The exhaustive Pattern Matching library for TypeScript.
398 lines (397 loc) • 19.6 kB
text/typescript
import type * as symbols from '../internals/symbols.cjs';
import { MergeUnion, Primitives, WithDefault } from './helpers.cjs';
import { None, Some, SelectionType } from './FindSelected.cjs';
import { matcher } from '../patterns.cjs';
import { ExtractPreciseValue } from './ExtractPreciseValue.cjs';
export type MatcherType = 'not' | 'optional' | 'or' | 'and' | 'array' | 'map' | 'set' | 'select' | 'default' | 'custom';
export type MatcherProtocol<input, narrowed, matcherType extends MatcherType, selections extends SelectionType, excluded> = {
match: <I>(value: I | input) => MatchResult;
getSelectionKeys?: () => string[];
matcherType?: matcherType;
};
export type MatchResult = {
matched: boolean;
selections?: Record<string, any>;
};
/**
* A `Matcher` is an object implementing the match
* protocol. It must define a `symbols.matcher` property
* which returns an object with a `match()` method, taking
* the input value and returning whether the pattern matches
* or not, along with optional selections.
*/
export interface Matcher<input, narrowed, matcherType extends MatcherType = 'default', selections extends SelectionType = None, excluded = narrowed> {
[matcher](): MatcherProtocol<input, narrowed, matcherType, selections, excluded>;
[symbols.isVariadic]?: boolean;
}
type PatternMatcher<input> = Matcher<input, unknown, any, any>;
export type MatchedValue<a, invpattern> = WithDefault<ExtractPreciseValue<a, invpattern>, a>;
export type AnyMatcher = Matcher<any, any, any, any, any>;
type UnknownMatcher = PatternMatcher<unknown>;
export type CustomP<input, pattern, narrowedOrFn> = Matcher<input, pattern, 'custom', None, narrowedOrFn>;
export type ArrayP<input, p> = Matcher<input, p, 'array'>;
export type OptionalP<input, p> = Matcher<input, p, 'optional'>;
export type MapP<input, pkey, pvalue> = Matcher<input, [pkey, pvalue], 'map'>;
export type SetP<input, p> = Matcher<input, p, 'set'>;
export type AndP<input, ps> = Matcher<input, ps, 'and'>;
export type OrP<input, ps> = Matcher<input, ps, 'or'>;
export type NotP<input, p> = Matcher<input, p, 'not'>;
export type GuardP<input, narrowed> = Matcher<input, narrowed>;
export type GuardExcludeP<input, narrowed, excluded> = Matcher<input, narrowed, 'default', None, excluded>;
export type SelectP<key extends string, input = unknown, p = Matcher<unknown, unknown>> = Matcher<input, p, 'select', Some<key>>;
export type AnonymousSelectP = SelectP<symbols.anonymousSelectKey>;
export interface Override<a> {
[symbols.override]: a;
}
export type UnknownProperties = {
readonly [k: PropertyKey]: unknown;
};
export type UnknownPattern = readonly [] | readonly [unknown, ...unknown[]] | readonly [...unknown[], unknown] | UnknownProperties | Primitives | UnknownMatcher;
/**
* `Pattern<a>` is the generic type for patterns matching a value of type `a`. A pattern can be any (nested) javascript value.
*
* They can also be wildcards, like `P._`, `P.string`, `P.number`,
* or other matchers, like `P.when(predicate)`, `P.not(pattern)`, etc.
*
* [Read the documentation for `P.Pattern` on GitHub](https://github.com/gvergnaud/ts-pattern#patterns)
*
* @example
* const pattern: P.Pattern<User> = { name: P.string }
*/
export type Pattern<a = unknown> = unknown extends a ? UnknownPattern : KnownPattern<a>;
type KnownPattern<a> = KnownPatternInternal<a>;
type KnownPatternInternal<a, objs = Exclude<a, Primitives | Map<any, any> | Set<any> | readonly any[]>, arrays = Extract<a, readonly any[]>, primitives = Extract<a, Primitives>> = primitives | PatternMatcher<a> | ([objs] extends [never] ? never : ObjectPattern<Readonly<MergeUnion<objs>>>) | ([arrays] extends [never] ? never : ArrayPattern<arrays>);
type ObjectPattern<a> = {
readonly [k in keyof a]?: Pattern<a[k]>;
} | never;
type ArrayPattern<a> = a extends readonly (infer i)[] ? a extends readonly [any, ...any] ? {
readonly [index in keyof a]: Pattern<a[index]>;
} : readonly [] | readonly [Pattern<i>, ...Pattern<i>[]] | readonly [...Pattern<i>[], Pattern<i>] : never;
export type AnyPattern = Chainable<GuardP<unknown, unknown>, never>;
export type StringPattern = StringChainable<GuardP<unknown, string>, never>;
export type NumberPattern = NumberChainable<GuardP<unknown, number>, never>;
export type BooleanPattern = Chainable<GuardP<unknown, boolean>, never>;
export type BigIntPattern = BigIntChainable<GuardP<unknown, bigint>, never>;
export type SymbolPattern = Chainable<GuardP<unknown, symbol>, never>;
export type NullishPattern = Chainable<GuardP<unknown, null | undefined>, never>;
export type NonNullablePattern = Chainable<GuardP<unknown, {}>, never>;
type MergeGuards<input, guard1, guard2> = [guard1, guard2] extends [
GuardExcludeP<any, infer narrowed1, infer excluded1>,
GuardExcludeP<any, infer narrowed2, infer excluded2>
] ? GuardExcludeP<input, narrowed1 & narrowed2, excluded1 & excluded2> : never;
export type Chainable<p, omitted extends string = never> = p & Omit<{
/**
* `.optional()` returns a pattern which matches if the
* key is undefined or if it is defined and the previous pattern matches its value.
*
* [Read the documentation for `P.optional` on GitHub](https://github.com/gvergnaud/ts-pattern#poptional-patterns)
*
* @example
* match(value)
* .with({ greeting: P.string.optional() }, () => 'will match { greeting?: string}')
*/
optional<input>(): Chainable<OptionalP<input, p>, omitted | 'optional'>;
/**
* `pattern.and(pattern)` returns a pattern that matches
* if the previous pattern and the next one match the input.
*
* [Read the documentation for `P.intersection` on GitHub](https://github.com/gvergnaud/ts-pattern#pintersection-patterns)
*
* @example
* match(value)
* .with(
* P.string.and(P.when(isUsername)),
* (username) => '...'
* )
*/
and<input, p2 extends Pattern<input>>(pattern: p2): Chainable<AndP<input, [p, p2]>, omitted>;
/**
* `pattern.or(pattern)` returns a pattern that matches
* if **either** the previous pattern or the next one match the input.
*
* [Read the documentation for `P.union` on GitHub](https://github.com/gvergnaud/ts-pattern#punion-patterns)
*
* @example
* match(value)
* .with(
* { value: P.string.or(P.number) },
* ({ value }) => 'value: number | string'
* )
*/
or<input, p2 extends Pattern<input>>(pattern: p2): Chainable<OrP<input, [p, p2]>, omitted>;
/**
* `P.select()` will inject this property into the handler function's arguments.
*
* [Read the documentation for `P.select` on GitHub](https://github.com/gvergnaud/ts-pattern#pselect-patterns)
*
* @example
* match<{ age: number }>(value)
* .with({ age: P.string.select() }, (age) => 'age: number')
*/
select<input>(): Chainable<SelectP<symbols.anonymousSelectKey, input, p>, omitted | 'select' | 'or' | 'and'>;
select<input, k extends string>(key: k): Chainable<SelectP<k, input, p>, omitted | 'select' | 'or' | 'and'>;
}, omitted>;
export type StringChainable<p extends Matcher<any, any, any, any, any>, omitted extends string = never> = Chainable<p, omitted> & Omit<{
/**
* `P.string.startsWith(start)` is a pattern, matching **strings** starting with `start`.
*
* [Read the documentation for `P.string.startsWith` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringstartsWith)
*
* @example
* match(value)
* .with(P.string.startsWith('A'), () => 'value starts with an A')
*/
startsWith<input, const start extends string>(start: start): StringChainable<MergeGuards<input, p, GuardP<unknown, `${start}${string}`>>, omitted | 'startsWith'>;
/**
* `P.string.endsWith(end)` is a pattern, matching **strings** ending with `end`.
*
* [Read the documentation for `P.string.endsWith` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringendsWith)
*
* @example
* match(value)
* .with(P.string.endsWith('!'), () => 'value ends with an !')
*/
endsWith<input, const end extends string>(end: end): StringChainable<MergeGuards<input, p, GuardP<unknown, `${string}${end}`>>, omitted | 'endsWith'>;
/**
* `P.string.minLength(min)` is a pattern, matching **strings** with at least `min` characters.
*
* [Read the documentation for `P.string.minLength` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringminLength)
*
* @example
* match(value)
* .with(P.string.minLength(10), () => 'string with more length <= 10')
*/
minLength<input, const min extends number>(min: min): StringChainable<MergeGuards<input, p, GuardExcludeP<unknown, string, never>>, omitted | 'minLength'>;
/**
* `P.string.length(len)` is a pattern, matching **strings** with exactly `len` characters.
*
* [Read the documentation for `P.string.length` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringlength)
*
* @example
* match(value)
* .with(P.string.length(10), () => 'strings with length === 10')
*/
length<input, const len extends number>(len: len): StringChainable<MergeGuards<input, p, GuardExcludeP<unknown, string, never>>, omitted | 'length' | 'minLength' | 'maxLength'>;
/**
* `P.string.maxLength(max)` is a pattern, matching **strings** with at most `max` characters.
*
* [Read the documentation for `P.string.maxLength` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringmaxLength)
*
* @example
* match(value)
* .with(P.string.maxLength(10), () => 'string with more length >= 10')
*/
maxLength<input, const max extends number>(max: max): StringChainable<MergeGuards<input, p, GuardExcludeP<unknown, string, never>>, omitted | 'maxLength'>;
/**
* `P.string.includes(substr)` is a pattern, matching **strings** containing `substr`.
*
* [Read the documentation for `P.string.includes` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringincludes)
*
* @example
* match(value)
* .with(P.string.includes('http'), () => 'value contains http')
*/
includes<input, const substr extends string>(substr: substr): StringChainable<MergeGuards<input, p, GuardExcludeP<unknown, string, never>>, omitted>;
/**
* `P.string.regex(expr)` is a pattern, matching **strings** that `expr` regular expression.
*
* [Read the documentation for `P.string.regex` on GitHub](https://github.com/gvergnaud/ts-pattern#pstringregex)
*
* @example
* match(value)
* .with(P.string.regex(/^https?:\/\//), () => 'url')
*/
regex<input, const expr extends string | RegExp>(expr: expr): StringChainable<MergeGuards<input, p, GuardExcludeP<unknown, string, never>>, omitted>;
}, omitted>;
export type NumberChainable<p, omitted extends string = never> = Chainable<p, omitted> & Omit<{
/**
* `P.number.between(min, max)` matches **number** between `min` and `max`,
* equal to min or equal to max.
*
* [Read the documentation for `P.number.between` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberbetween)
*
* @example
* match(value)
* .with(P.number.between(0, 10), () => '0 <= numbers <= 10')
*/
between<input, const min extends number, const max extends number>(min: min, max: max): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted>;
/**
* `P.number.lt(max)` matches **number** smaller than `max`.
*
* [Read the documentation for `P.number.lt` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberlt)
*
* @example
* match(value)
* .with(P.number.lt(10), () => 'numbers < 10')
*/
lt<input, const max extends number>(max: max): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted>;
/**
* `P.number.gt(min)` matches **number** greater than `min`.
*
* [Read the documentation for `P.number.gt` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbergt)
*
* @example
* match(value)
* .with(P.number.gt(10), () => 'numbers > 10')
*/
gt<input, const min extends number>(min: min): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted>;
/**
* `P.number.lte(max)` matches **number** smaller than or equal to `max`.
*
* [Read the documentation for `P.number.lte` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberlte)
*
* @example
* match(value)
* .with(P.number.lte(10), () => 'numbers <= 10')
*/
lte<input, const max extends number>(max: max): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted>;
/**
* `P.number.gte(min)` matches **number** greater than or equal to `min`.
*
* [Read the documentation for `P.number.gte` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbergte)
*
* @example
* match(value)
* .with(P.number.gte(10), () => 'numbers >= 10')
*/
gte<input, const min extends number>(min: min): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted>;
/**
* `P.number.int` matches **integer** numbers.
*
* [Read the documentation for `P.number.int` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberint)
*
* @example
* match(value)
* .with(P.number.int, () => 'an integer')
*/
int<input>(): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted | 'int'>;
/**
* `P.number.finite` matches **finite numbers**.
*
* [Read the documentation for `P.number.finite` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberfinite)
*
* @example
* match(value)
* .with(P.number.finite, () => 'not Infinity')
*/
finite<input>(): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted | 'finite'>;
/**
* `P.number.positive` matches **positive** numbers.
*
* [Read the documentation for `P.number.positive` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberpositive)
*
* @example
* match(value)
* .with(P.number.positive, () => 'number > 0')
*/
positive<input>(): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted | 'positive' | 'negative'>;
/**
* `P.number.negative` matches **negative** numbers.
*
* [Read the documentation for `P.number.negative` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbernegative)
*
* @example
* match(value)
* .with(P.number.negative, () => 'number < 0')
*/
negative<input>(): NumberChainable<MergeGuards<input, p, GuardExcludeP<unknown, number, never>>, omitted | 'positive' | 'negative' | 'negative'>;
}, omitted>;
export type BigIntChainable<p, omitted extends string = never> = Chainable<p, omitted> & Omit<{
/**
* `P.bigint.between(min, max)` matches **bigint** between `min` and `max`,
* equal to min or equal to max.
*
* [Read the documentation for `P.bigint.between` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberbetween)
*
* @example
* match(value)
* .with(P.bigint.between(0, 10), () => '0 <= numbers <= 10')
*/
between<input, const min extends bigint, const max extends bigint>(min: min, max: max): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted>;
/**
* `P.bigint.lt(max)` matches **bigint** smaller than `max`.
*
* [Read the documentation for `P.bigint.lt` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberlt)
*
* @example
* match(value)
* .with(P.bigint.lt(10), () => 'numbers < 10')
*/
lt<input, const max extends bigint>(max: max): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted>;
/**
* `P.bigint.gt(min)` matches **bigint** greater than `min`.
*
* [Read the documentation for `P.bigint.gt` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbergt)
*
* @example
* match(value)
* .with(P.bigint.gt(10), () => 'numbers > 10')
*/
gt<input, const min extends bigint>(min: min): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted>;
/**
* `P.bigint.lte(max)` matches **bigint** smaller than or equal to `max`.
*
* [Read the documentation for `P.bigint.lte` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberlte)
*
* @example
* match(value)
* .with(P.bigint.lte(10), () => 'bigints <= 10')
*/
lte<input, const max extends bigint>(max: max): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted>;
/**
* `P.bigint.gte(min)` matches **bigint** greater than or equal to `min`.
*
* [Read the documentation for `P.bigint.gte` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbergte)
*
* @example
* match(value)
* .with(P.bigint.gte(10), () => 'bigints >= 10')
*/
gte<input, const min extends bigint>(min: min): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted>;
/**
* `P.bigint.positive` matches **positive** bigints.
*
* [Read the documentation for `P.bigint.positive` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumberpositive)
*
* @example
* match(value)
* .with(P.bigint.positive, () => 'bigint > 0')
*/
positive<input>(): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted | 'positive' | 'negative'>;
/**
* `P.bigint.negative` matches **negative** bigints.
*
* [Read the documentation for `P.bigint.negative` on GitHub](https://github.com/gvergnaud/ts-pattern#pnumbernegative)
*
* @example
* match(value)
* .with(P.bigint.negative, () => 'bigint < 0')
*/
negative<input>(): BigIntChainable<MergeGuards<input, p, GuardExcludeP<unknown, bigint, never>>, omitted | 'positive' | 'negative' | 'negative'>;
}, omitted>;
export type Variadic<pattern> = pattern & Iterable<pattern>;
export type ArrayChainable<pattern, omitted extends string = never> = Variadic<pattern> & Omit<{
/**
* `.optional()` returns a pattern which matches if the
* key is undefined or if it is defined and the previous pattern matches its value.
*
* [Read the documentation for `P.optional` on GitHub](https://github.com/gvergnaud/ts-pattern#poptional-patterns)
*
* @example
* match(value)
* .with({ greeting: P.string.optional() }, () => 'will match { greeting?: string}')
*/
optional<input>(): ArrayChainable<OptionalP<input, pattern>, omitted | 'optional'>;
/**
* `P.select()` will inject this property into the handler function's arguments.
*
* [Read the documentation for `P.select` on GitHub](https://github.com/gvergnaud/ts-pattern#pselect-patterns)
*
* @example
* match<{ age: number }>(value)
* .with({ age: P.string.select() }, (age) => 'age: number')
*/
select<input>(): ArrayChainable<SelectP<symbols.anonymousSelectKey, input, pattern>, omitted | 'select'>;
select<input, k extends string>(key: k): ArrayChainable<SelectP<k, input, pattern>, omitted | 'select'>;
}, omitted>;
export {};