Skip to main content
Module

x/fuzzy_octo_guacamole/patterns.ts

A playground for testing CI setup that deploys on Deno and NPM
Latest
File
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546
import { matchPattern, getSelectionKeys, flatMap } from './internals/helpers.ts';import * as symbols from './internals/symbols.ts';import { matcher } from './internals/symbols.ts';import { isMatching } from './is-matching.ts';import { ExtractPreciseValue } from './types/ExtractPreciseValue.ts';import { Fn } from './types/helpers.ts';import { InvertPattern } from './types/InvertPattern.ts';import { Pattern, UnknownPattern, OptionalP, ArrayP, MapP, SetP, AndP, OrP, NotP, GuardP, SelectP, AnonymousSelectP, GuardExcludeP, CustomP, Matcher,} from './types/Pattern.ts';
export type { Pattern, Fn as unstable_Fn };
export { matcher };
/** * @experimental * A `Matchable` is an object implementing * the Matcher Protocol. It must have a `[P.matcher]: P.Matcher<NarrowFn>` * key, which defines how this object should be matched by TS-Pattern. * * Note that this api is unstable. * * @example * ```ts * class Some<T> implements P.unstable_Matchable { * [P.matcher](): P.unstable_Matcher<Some<T>> * } * ``` */export type unstable_Matchable< narrowedOrFn, input = unknown, pattern = never> = CustomP<input, pattern, narrowedOrFn>;
/** * @experimental * A `Matcher` is an object with `match` function, which * defines how this object should be matched by TS-Pattern. * * Note that this api is unstable. * * @example * ```ts * class Some<T> implements P.unstable_Matchable { * [P.matcher](): P.unstable_Matcher<Some<T>> * } * ``` */export type unstable_Matcher< narrowedOrFn, input = unknown, pattern = never> = ReturnType<CustomP<input, pattern, narrowedOrFn>[matcher]>;
/** * `P.infer<typeof somePattern>` will return the type of the value * matched by this pattern. * * [Read the documentation for `P.infer` on GitHub](https://github.com/gvergnaud/ts-pattern#Pinfer) * * @example * const userPattern = { name: P.string } * type User = P.infer<typeof userPattern> */export type infer<p extends Pattern<any>> = InvertPattern<p, unknown>;
export type narrow<i, p extends Pattern<any>> = ExtractPreciseValue< i, InvertPattern<p, i>>;
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 >;
function chainable<p extends Matcher<any, any, any, any, any>>( pattern: p): Chainable<p> { return Object.assign(pattern, { optional: () => optional(pattern), and: (p2: any) => intersection(pattern, p2), or: (p2: any) => union(pattern, p2), select: (key: any) => key === undefined ? select(pattern) : select(key, pattern), }) as Chainable<p>;}
type Variadic<p> = p & Iterable<p>;
const variadic = <p extends {}>(pattern: p): Variadic<p> => Object.assign(pattern, { *[Symbol.iterator]() { yield Object.assign(pattern, { [symbols.isVariadic]: true, }); }, });
type ArrayChainable<p, omitted extends string = never> = Variadic<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>(): ArrayChainable< OptionalP<input, p>, 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, p>, omitted | 'select' >; select<input, k extends string>( key: k ): ArrayChainable<SelectP<k, input, p>, omitted | 'select'>; }, omitted >;
function arrayChainable<p extends Matcher<any, any, any, any, any>>( pattern: p): ArrayChainable<p> { return Object.assign(variadic(pattern), { optional: () => arrayChainable(optional(pattern)), select: (key: any) => arrayChainable( key === undefined ? select(pattern) : select(key, pattern) ), }) as any;}
/** * `P.optional(subpattern)` takes a sub pattern and returns a pattern which matches if the * key is undefined or if it is defined and the sub 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.optional('Hello') }, () => 'will match { greeting?: "Hello" }') */export function optional< input, const p extends unknown extends input ? UnknownPattern : Pattern<input>>(pattern: p): Chainable<OptionalP<input, p>, 'optional'> { return chainable({ [matcher]() { return { match: <UnknownInput>(value: UnknownInput | input) => { let selections: Record<string, unknown[]> = {}; const selector = (key: string, value: any) => { selections[key] = value; }; if (value === undefined) { getSelectionKeys(pattern).forEach((key) => selector(key, undefined) ); return { matched: true, selections }; } const matched = matchPattern(pattern, value, selector); return { matched, selections }; }, getSelectionKeys: () => getSelectionKeys(pattern), matcherType: 'optional', }; }, });}
type UnwrapArray<xs> = xs extends readonly (infer x)[] ? x : never;
type UnwrapSet<xs> = xs extends Set<infer x> ? x : never;
type UnwrapMapKey<xs> = xs extends Map<infer k, any> ? k : never;
type UnwrapMapValue<xs> = xs extends Map<any, infer v> ? v : never;
type WithDefault<a, b> = [a] extends [never] ? b : a;
/** * `P.array(subpattern)` takes a sub pattern and returns a pattern, which matches * arrays if all their elements match the sub pattern. * * [Read the documentation for `P.array` on GitHub](https://github.com/gvergnaud/ts-pattern#Parray-patterns) * * @example * match(value) * .with({ users: P.array({ name: P.string }) }, () => 'will match { name: string }[]') */export function array<input>(): ArrayChainable<ArrayP<input, unknown>>;export function array< input, const p extends Pattern<WithDefault<UnwrapArray<input>, unknown>>>(pattern: p): ArrayChainable<ArrayP<input, p>>;export function array( ...args: [pattern?: any]): ArrayChainable<ArrayP<any, any>> { return arrayChainable({ [matcher]() { return { match: (value: any) => { if (!Array.isArray(value)) return { matched: false };
if (args.length === 0) return { matched: true };
const pattern = args[0]; let selections: Record<string, unknown[]> = {};
if (value.length === 0) { getSelectionKeys(pattern).forEach((key) => { selections[key] = []; }); return { matched: true, selections }; }
const selector = (key: string, value: unknown) => { selections[key] = (selections[key] || []).concat([value]); };
const matched = value.every((v) => matchPattern(pattern, v, selector) );
return { matched, selections }; }, getSelectionKeys: () => args.length === 0 ? [] : getSelectionKeys(args[0]), }; }, });}
/** * `P.set(subpattern)` takes a sub pattern and returns a pattern that matches * sets if all their elements match the sub pattern. * * [Read `P.set` documentation on GitHub](https://github.com/gvergnaud/ts-pattern#Pset-patterns) * * @example * match(value) * .with({ users: P.set(P.string) }, () => 'will match Set<string>') */export function set<input>(): Chainable<SetP<input, unknown>>;export function set< input, const p extends Pattern<WithDefault<UnwrapSet<input>, unknown>>>(pattern: p): Chainable<SetP<input, p>>;export function set< input, const p extends Pattern<WithDefault<UnwrapSet<input>, unknown>>>(...args: [pattern?: p]): Chainable<SetP<input, p>> { return chainable({ [matcher]() { return { match: <UnknownInput>(value: UnknownInput | input) => { if (!(value instanceof Set)) return { matched: false };
let selections: Record<string, unknown[]> = {};
if (value.size === 0) { return { matched: true, selections }; }
if (args.length === 0) return { matched: true };
const selector = (key: string, value: unknown) => { selections[key] = (selections[key] || []).concat([value]); };
const pattern = args[0];
const matched = setEvery(value, (v) => matchPattern(pattern, v, selector) );
return { matched, selections }; }, getSelectionKeys: () => args.length === 0 ? [] : getSelectionKeys(args[0]), }; }, });}
const setEvery = <T>(set: Set<T>, predicate: (value: T) => boolean) => { for (const value of set) { if (predicate(value)) continue; return false; } return true;};
/** * `P.set(subpattern)` takes a sub pattern and returns a pattern that matches * sets if all their elements match the sub pattern. * * [Read `P.set` documentation on GitHub](https://github.com/gvergnaud/ts-pattern#Pset-patterns) * * @example * match(value) * .with({ users: P.set(P.string) }, () => 'will match Set<string>') */export function map<input>(): Chainable<MapP<input, unknown, unknown>>;export function map< input, const pkey extends Pattern<WithDefault<UnwrapMapKey<input>, unknown>>, const pvalue extends Pattern<WithDefault<UnwrapMapValue<input>, unknown>>>(patternKey: pkey, patternValue: pvalue): Chainable<MapP<input, pkey, pvalue>>;export function map< input, const pkey extends Pattern<WithDefault<UnwrapMapKey<input>, unknown>>, const pvalue extends Pattern<WithDefault<UnwrapMapValue<input>, unknown>>>( ...args: [patternKey?: pkey, patternValue?: pvalue]): Chainable<MapP<input, pkey, pvalue>> { return chainable({ [matcher]() { return { match: <UnknownInput>(value: UnknownInput | input) => { if (!(value instanceof Map)) return { matched: false };
let selections: Record<string, unknown[]> = {};
if (value.size === 0) { return { matched: true, selections }; }
const selector = (key: string, value: unknown) => { selections[key] = (selections[key] || []).concat([value]); };
if (args.length === 0) return { matched: true }; if (args.length === 1) { throw new Error( `\`P.map\` wasn\'t given enough arguments. Expected (key, value), received ${args[0]?.toString()}` ); } const [patternKey, patternValue] = args;
const matched = mapEvery(value, (v, k) => { const keyMatch = matchPattern(patternKey, k, selector); const valueMatch = matchPattern(patternValue, v, selector); return keyMatch && valueMatch; });
return { matched, selections }; }, getSelectionKeys: () => args.length === 0 ? [] : [...getSelectionKeys(args[0]), ...getSelectionKeys(args[1])], }; }, });}
const mapEvery = <K, T>( map: Map<K, T>, predicate: (value: T, key: K) => boolean) => { for (const [key, value] of map.entries()) { if (predicate(value, key)) continue; return false; } return true;};
/** * `P.intersection(...patterns)` returns a pattern which matches * only if **every** patterns provided in parameter match the input. * * [Read the documentation for `P.intersection` on GitHub](https://github.com/gvergnaud/ts-pattern#Pintersection-patterns) * * @example * match(value) * .with( * { * user: P.intersection( * { firstname: P.string }, * { lastname: P.string }, * { age: P.when(age => age > 21) } * ) * }, * ({ user }) => 'will match { firstname: string, lastname: string, age: number } if age > 21' * ) */export function intersection< input, const ps extends readonly [Pattern<input>, ...Pattern<input>[]]>(...patterns: ps): Chainable<AndP<input, ps>> { return chainable({ [matcher]: () => ({ match: (value) => { let selections: Record<string, unknown[]> = {}; const selector = (key: string, value: any) => { selections[key] = value; }; const matched = (patterns as readonly UnknownPattern[]).every((p) => matchPattern(p, value, selector) ); return { matched, selections }; }, getSelectionKeys: () => flatMap(patterns as readonly UnknownPattern[], getSelectionKeys), matcherType: 'and', }), });}
/** * `P.union(...patterns)` returns a pattern which matches * if **at least one** of the patterns provided in parameter match the input. * * [Read the documentation for `P.union` on GitHub](https://github.com/gvergnaud/ts-pattern#Punion-patterns) * * @example * match(value) * .with( * { type: P.union('a', 'b', 'c') }, * ({ type }) => 'will match { type: "a" | "b" | "c" }' * ) */export function union< input, const ps extends readonly [Pattern<input>, ...Pattern<input>[]]>(...patterns: ps): Chainable<OrP<input, ps>> { return chainable({ [matcher]: () => ({ match: <UnknownInput>(value: UnknownInput | input) => { let selections: Record<string, unknown[]> = {}; const selector = (key: string, value: any) => { selections[key] = value; }; flatMap( patterns as readonly UnknownPattern[], getSelectionKeys ).forEach((key) => selector(key, undefined)); const matched = (patterns as readonly UnknownPattern[]).some((p) => matchPattern(p, value, selector) ); return { matched, selections }; }, getSelectionKeys: () => flatMap(patterns as readonly UnknownPattern[], getSelectionKeys), matcherType: 'or', }), });}
/** * `P.not(pattern)` returns a pattern which matches if the sub pattern * doesn't match. * * [Read the documentation for `P.not` on GitHub](https://github.com/gvergnaud/ts-pattern#Pnot-patterns) * * @example * match<{ a: string | number }>(value) * .with({ a: P.not(P.string) }, (x) => 'will match { a: number }' * ) */
export function not<input, const p extends Pattern<input> | UnknownPattern>( pattern: p): Chainable<NotP<input, p>> { return chainable({ [matcher]: () => ({ match: <UnknownInput>(value: UnknownInput | input) => ({ matched: !matchPattern(pattern, value, () => {}), }), getSelectionKeys: () => [], matcherType: 'not', }), });}
/** * `P.when((value) => boolean)` returns a pattern which matches * if the predicate returns true for the current input. * * [Read the documentation for `P.when` on GitHub](https://github.com/gvergnaud/ts-pattern#Pwhen-patterns) * * @example * match<{ age: number }>(value) * .with({ age: P.when(age => age > 21) }, (x) => 'will match if value.age > 21' * ) */export function when<input, p extends (value: input) => unknown>( predicate: p): GuardP< input, p extends (value: any) => value is infer narrowed ? narrowed : never>;export function when<input, narrowed extends input, excluded>( predicate: (input: input) => input is narrowed): GuardExcludeP<input, narrowed, excluded>;export function when<input, p extends (value: input) => unknown>( predicate: p): GuardP< input, p extends (value: any) => value is infer narrowed ? narrowed : never> { return { [matcher]: () => ({ match: <UnknownInput>(value: UnknownInput | input) => ({ matched: Boolean(predicate(value as input)), }), }), };}
/** * `P.select()` is a pattern which will always match, * and will inject the selected piece of input in the handler function. * * [Read the documentation for `P.select` on GitHub](https://github.com/gvergnaud/ts-pattern#Pselect-patterns) * * @example * match<{ age: number }>(value) * .with({ age: P.select() }, (age) => 'age: number' * ) */export function select(): Chainable<AnonymousSelectP, 'select' | 'or' | 'and'>;export function select< input, const patternOrKey extends | string | (unknown extends input ? UnknownPattern : Pattern<input>)>( patternOrKey: patternOrKey): patternOrKey extends string ? Chainable<SelectP<patternOrKey, 'select' | 'or' | 'and'>> : Chainable< SelectP<symbols.anonymousSelectKey, input, patternOrKey>, 'select' | 'or' | 'and' >;export function select< input, const p extends unknown extends input ? UnknownPattern : Pattern<input>, const k extends string>(key: k, pattern: p): Chainable<SelectP<k, input, p>, 'select' | 'or' | 'and'>;export function select( ...args: [keyOrPattern?: unknown | string, pattern?: unknown]): Chainable<SelectP<string>, 'select' | 'or' | 'and'> { const key: string | undefined = typeof args[0] === 'string' ? args[0] : undefined; const pattern: unknown = args.length === 2 ? args[1] : typeof args[0] === 'string' ? undefined : args[0]; return chainable({ [matcher]() { return { match: (value) => { let selections: Record<string, unknown> = { [key ?? symbols.anonymousSelectKey]: value, }; const selector = (key: string, value: any) => { selections[key] = value; }; return { matched: pattern === undefined ? true : matchPattern(pattern, value, selector), selections: selections, }; }, getSelectionKeys: () => [key ?? symbols.anonymousSelectKey].concat( pattern === undefined ? [] : getSelectionKeys(pattern) ), }; }, });}
function isUnknown(x: unknown): x is unknown { return true;}
function isNumber<T>(x: T | number): x is number { return typeof x === 'number';}
function isString<T>(x: T | string): x is string { return typeof x === 'string';}
function isBoolean<T>(x: T | boolean): x is boolean { return typeof x === 'boolean';}
function isBigInt<T>(x: T | bigint): x is bigint { return typeof x === 'bigint';}
function isSymbol<T>(x: T | symbol): x is symbol { return typeof x === 'symbol';}
function isNullish<T>(x: T | null | undefined): x is null | undefined { return x === null || x === undefined;}
type AnyConstructor = abstract new (...args: any[]) => any;
function isInstanceOf<T extends AnyConstructor>(classConstructor: T) { return (val: unknown): val is InstanceType<T> => val instanceof classConstructor;}
// These aliases could be inferred, but lead to nicer display names in IDEs.type AnyPattern = Chainable<GuardP<unknown, unknown>, never>;type StringPattern = StringChainable<GuardP<unknown, string>, never>;type NumberPattern = NumberChainable<GuardP<unknown, number>, never>;type BooleanPattern = Chainable<GuardP<unknown, boolean>, never>;type BigIntPattern = BigIntChainable<GuardP<unknown, bigint>, never>;type SymbolPattern = Chainable<GuardP<unknown, symbol>, never>;type NullishPattern = Chainable<GuardP<unknown, null | undefined>, never>;
/** * `P.any` is a wildcard pattern, matching **any value**. * * [Read the documentation for `P.any` on GitHub](https://github.com/gvergnaud/ts-pattern#P_-wildcard) * * @example * match(value) * .with(P.any, () => 'will always match') */export const any: AnyPattern = chainable(when(isUnknown));
/** * `P._` is a wildcard pattern, matching **any value**. * It's an alias to `P.any`. * * [Read the documentation for `P._` on GitHub](https://github.com/gvergnaud/ts-pattern#P_-wildcard) * * @example * match(value) * .with(P._, () => 'will always match') */export const _ = any;
/** * `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') */
const startsWith = <input, const start extends string>( start: start): GuardP<input, `${start}${string}`> => when((value) => isString(value) && value.startsWith(start));
/** * `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 !') */const endsWith = <input, const end extends string>( end: end): GuardP<input, `${string}${end}`> => when((value) => isString(value) && value.endsWith(end));
/** * `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') */const minLength = <const min extends number>(min: min) => when((value) => isString(value) && value.length >= min);
/** * `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') */const maxLength = <const max extends number>(max: max) => when((value) => isString(value) && value.length <= max);
/** * `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') */const includes = <input, const substr extends string>( substr: substr): GuardExcludeP<input, string, never> => when((value) => isString(value) && value.includes(substr));
/** * `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') */const regex = <input, const expr extends string | RegExp>( expr: expr): GuardExcludeP<input, string, never> => when((value) => isString(value) && Boolean(value.match(expr)));
type MaybeAnd<omitted, input, p1, p2> = [omitted] extends [never] ? p2 : AndP<input, [p1, p2]>;
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< MaybeAnd<omitted, input, p, GuardP<input, `${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< MaybeAnd<omitted, input, p, GuardP<input, `${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< MaybeAnd<omitted, input, p, GuardExcludeP<input, string, never>>, omitted | 'minLength' >; /** * `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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, string, never>>, omitted >; }, omitted >;
const stringChainable = <p extends Matcher<any, any, any, any, any>>( pattern: p): StringChainable<p> => Object.assign(chainable(pattern), { startsWith: (str: string) => stringChainable(intersection(pattern, startsWith(str))), endsWith: (str: string) => stringChainable(intersection(pattern, endsWith(str))), minLength: (min: number) => stringChainable(intersection(pattern, minLength(min))), maxLength: (max: number) => stringChainable(intersection(pattern, maxLength(max))), includes: (str: string) => stringChainable(intersection(pattern, includes(str))), regex: (str: string) => stringChainable(intersection(pattern, regex(str))), }) as any;
/** * `P.string` is a wildcard pattern, matching any **string**. * * [Read the documentation for `P.string` on GitHub](https://github.com/gvergnaud/ts-pattern#Pstring-wildcard) * * @example * match(value) * .with(P.string, () => 'will match on strings') */export const string: StringPattern = stringChainable(when(isString));
/** * `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') */export const between = < input, const min extends number, const max extends number>( min: min, max: max): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && min <= value && max >= value);
/** * `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') */export const lt = <input, const max extends number>( max: max): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value < max);
/** * `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') */export const gt = <input, const min extends number>( min: min): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value > min);
/** * `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') */export const lte = <input, const max extends number>( max: max): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value <= max);
/** * `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') */export const gte = <input, const min extends number>( min: min): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value >= min);
/** * `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') */export const int = <input>(): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && Number.isInteger(value));
/** * `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') */export const finite = <input>(): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && Number.isFinite(value));
/** * `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') */export const positive = <input>(): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value > 0);
/** * `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') */export const negative = <input>(): GuardExcludeP<input, number, never> => when((value) => isNumber(value) && value < 0);
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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, number, never>>, omitted | 'positive' | 'negative' | 'negative' >; }, omitted >;
const numberChainable = <p extends Matcher<any, any, any, any, any>>( pattern: p): NumberChainable<p> => Object.assign(chainable(pattern), { between: (min: number, max: number) => numberChainable(intersection(pattern, between(min, max))), lt: (max: number) => numberChainable(intersection(pattern, lt(max))), gt: (min: number) => numberChainable(intersection(pattern, gt(min))), lte: (max: number) => numberChainable(intersection(pattern, lte(max))), gte: (min: number) => numberChainable(intersection(pattern, gte(min))), int: () => numberChainable(intersection(pattern, int())), finite: () => numberChainable(intersection(pattern, finite())), positive: () => numberChainable(intersection(pattern, positive())), negative: () => numberChainable(intersection(pattern, negative())), }) as any;
/** * `P.number` is a wildcard pattern, matching any **number**. * * [Read the documentation for `P.number` on GitHub](https://github.com/gvergnaud/ts-pattern#Pnumber-wildcard) * * @example * match(value) * .with(P.number, () => 'will match on numbers') */export const number: NumberPattern = numberChainable(when(isNumber));
/** * `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') */export const betweenBigInt = < input, const min extends bigint, const max extends bigint>( min: min, max: max): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && min <= value && max >= value);
/** * `P.bigint.lt(max)` matches **bigint** smaller than `max`. * * [Read the documentation for `P.bigint.lt` on GitHub](https://github.com/gvergnaud/ts-pattern#bigintlt) * * @example * match(value) * .with(P.bigint.lt(10), () => 'numbers < 10') */export const ltBigInt = <input, const max extends bigint>( max: max): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value < max);
/** * `P.bigint.gt(min)` matches **bigint** greater than `min`. * * [Read the documentation for `P.bigint.gt` on GitHub](https://github.com/gvergnaud/ts-pattern#bigintgt) * * @example * match(value) * .with(P.bigint.gt(10), () => 'numbers > 10') */export const gtBigInt = <input, const min extends bigint>( min: min): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value > min);
/** * `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#bigintlte) * * @example * match(value) * .with(P.bigint.lte(10), () => 'bigints <= 10') */export const lteBigInt = <input, const max extends bigint>( max: max): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value <= max);
/** * `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#Pbigintgte) * * @example * match(value) * .with(P.bigint.gte(10), () => 'bigints >= 10') */export const gteBigInt = <input, const min extends bigint>( min: min): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value >= min);
/** * `P.bigint.positive` matches **positive** bigints. * * [Read the documentation for `P.bigint.positive` on GitHub](https://github.com/gvergnaud/ts-pattern#Pbigintpositive) * * @example * match(value) * .with(P.bigint.positive, () => 'bigint > 0') */export const positiveBigInt = <input>(): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value > 0);
/** * `P.bigint.negative` matches **negative** bigints. * * [Read the documentation for `P.bigint.negative` on GitHub](https://github.com/gvergnaud/ts-pattern#Pbigintnegative) * * @example * match(value) * .with(P.bigint.negative, () => 'bigint < 0') */export const negativeBigInt = <input>(): GuardExcludeP<input, bigint, never> => when((value) => isBigInt(value) && value < 0);
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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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#bigintlt) * * @example * match(value) * .with(P.bigint.lt(10), () => 'numbers < 10') */ lt<input, const max extends bigint>( max: max ): BigIntChainable< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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#bigintgt) * * @example * match(value) * .with(P.bigint.gt(10), () => 'numbers > 10') */ gt<input, const min extends bigint>( min: min ): BigIntChainable< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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#bigintlte) * * @example * match(value) * .with(P.bigint.lte(10), () => 'bigints <= 10') */ lte<input, const max extends bigint>( max: max ): BigIntChainable< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, 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< MaybeAnd<omitted, input, p, GuardExcludeP<input, bigint, never>>, omitted | 'positive' | 'negative' | 'negative' >; }, omitted >;
const bigintChainable = <p extends Matcher<any, any, any, any, any>>( pattern: p): BigIntChainable<p> => Object.assign(chainable(pattern), { between: (min: bigint, max: bigint) => bigintChainable(intersection(pattern, betweenBigInt(min, max))), lt: (max: bigint) => bigintChainable(intersection(pattern, ltBigInt(max))), gt: (min: bigint) => bigintChainable(intersection(pattern, gtBigInt(min))), lte: (max: bigint) => bigintChainable(intersection(pattern, lteBigInt(max))), gte: (min: bigint) => bigintChainable(intersection(pattern, gteBigInt(min))), positive: () => bigintChainable(intersection(pattern, positiveBigInt())), negative: () => bigintChainable(intersection(pattern, negativeBigInt())), }) as any;
/** * `P.bigint` is a wildcard pattern, matching any **bigint**. * * [Read the documentation for `P.bigint` on GitHub](https://github.com/gvergnaud/ts-pattern#bigint-wildcard) * * @example * .with(P.bigint, () => 'will match on bigints') */export const bigint: BigIntPattern = bigintChainable(when(isBigInt));
/** * `P.boolean` is a wildcard pattern, matching any **boolean**. * * [Read the documentation for `P.boolean` on GitHub](https://github.com/gvergnaud/ts-pattern#boolean-wildcard) * * @example * .with(P.boolean, () => 'will match on booleans') */export const boolean: BooleanPattern = chainable(when(isBoolean));
/** * `P.symbol` is a wildcard pattern, matching any **symbol**. * * [Read the documentation for `P.symbol` on GitHub](https://github.com/gvergnaud/ts-pattern#symbol-wildcard) * * @example * .with(P.symbol, () => 'will match on symbols') */export const symbol: SymbolPattern = chainable(when(isSymbol));
/** * `P.nullish` is a wildcard pattern, matching **null** or **undefined**. * * [Read the documentation for `P.nullish` on GitHub](https://github.com/gvergnaud/ts-pattern#nullish-wildcard) * * @example * .with(P.nullish, () => 'will match on null or undefined') */export const nullish: NullishPattern = chainable(when(isNullish));
/** * `P.instanceOf(SomeClass)` is a pattern matching instances of a given class. * * [Read the documentation for `P.instanceOf` on GitHub](https://github.com/gvergnaud/ts-pattern#Pinstanceof-patterns) * * @example * .with(P.instanceOf(SomeClass), () => 'will match on SomeClass instances') */export function instanceOf<T extends AnyConstructor>( classConstructor: T): Chainable<GuardP<unknown, InstanceType<T>>> { return chainable(when(isInstanceOf(classConstructor)));}
/** * `P.shape(somePattern)` lets you call methods like `.optional()`, `.and`, `.or` and `.select()` * On structural patterns, like objects and arrays. * * [Read the documentation for `P.shape` on GitHub](https://github.com/gvergnaud/ts-pattern#Pshape-patterns) * * @example * .with( * { * state: P.shape({ status: "success" }).optional().select() * }, * (state) => 'match the success state, or undefined.' * ) */export function shape<input, const p extends Pattern<input>>( pattern: p): Chainable<GuardP<input, InvertPattern<p, input>>>;export function shape(pattern: UnknownPattern) { return chainable(when(isMatching(pattern)));}