import type { ExtractPlucked } from 'match-iz' /** * Element type stored in a **match** bucket when sifting values of type `T` * with pattern `P`. * * - `P` contains `pluck(...)` → plucked payload (`ExtractPlucked

`) * - `P` is a type guard `(x) => x is U` → `Extract` * - otherwise → full element `T` (structural match / no transform) * * Remainder buckets always keep full elements `T` (or `Exclude` via dedicated * type-guard overloads). */ export type MatchPayload = [ExtractPlucked

] extends [never] ? P extends (value: any) => value is infer U ? Extract : T : ExtractPlucked

/** * Result of sifting an array of `T` against pattern tuple `P`. * - 0 patterns → `[T[], T[]]` (empty matches + full remainder) * - N patterns → N match buckets (`MatchPayload` each) + 1 remainder `T[]` */ export type ArraySiftResult< T, P extends readonly unknown[] > = P extends readonly [] ? [T[], T[]] : [...{ [K in keyof P]: Array> }, T[]] /** * Result of sifting a `Set` against pattern tuple `P`. * Match buckets use {@link MatchPayload}; remainder stays `Set`. */ export type SetSiftResult< T, P extends readonly unknown[] > = P extends readonly [] ? [Set, Set] : [...{ [K in keyof P]: Set> }, Set] /** * Result of sifting a `Map` against pattern tuple `P`. * Patterns match **values**; match buckets use {@link MatchPayload} on `V`. */ export type MapSiftResult< K, V, P extends readonly unknown[] > = P extends readonly [] ? [Map, Map] : [...{ [I in keyof P]: Map> }, Map] /** * Result of sifting an `IterableIterator` (e.g. generator). * - 0 patterns → `[undefined, iterator]` (runtime passthru / otherwise) * - N patterns → N+1 **arrays** (match arms use {@link MatchPayload}) */ export type IterableSiftResult< T, P extends readonly unknown[] > = P extends readonly [] ? [undefined, IterableIterator] : [...{ [K in keyof P]: Array> }, T[]] /** * One pojo bucket: same keys as `T` may appear, with the same value types. * Runtime only copies matching entries, so every key is optional. */ export type PojoBucket = { [K in keyof T]?: T[K] } /** * One match bucket for pojo value-patterns: keys optional, values may be * {@link MatchPayload} (pluck-aware). */ export type PojoPatternBucket = { [K in keyof T]?: MatchPayload } /** * Per-key schema match bucket: each key uses MatchPayload against its schema slot. */ export type PojoSchemaMatchBucket = { [K in keyof T]?: K extends keyof S ? MatchPayload : T[K] } /** * Result of sifting a pojo of type `T` against pattern tuple `P`. * - 0 patterns → `[{}, T]` passthru (empty matches + full input) * - N patterns → N match buckets (pluck-aware) + 1 remainder `PojoBucket` * * Note: multi-pattern pojo form is `sift(pojo, [p1, p2, …])` (one array arg), * not rest args (unlike arrays). */ export type PojoSiftResult< T extends object, P extends readonly unknown[] > = P extends readonly [] ? [PojoBucket, T] : [...{ [K in keyof P]: PojoPatternBucket }, PojoBucket] /** Type predicate used by single- and multi-guard sift/siftr overloads. */ export type TypeGuard = (value: any) => value is U /** * Multi type-guard partition of element type `T`. * Match buckets are `Extract`; remainder is `Exclude`. * First matching guard wins at runtime (same order as arguments). */ export type GuardPartition = [U3] extends [ never ] ? [U2] extends [never] ? [Array>, Array>] : [ Array>, Array>, Array> ] : [ Array>, Array>, Array>, Array> ] export type GuardSetPartition = [U3] extends [ never ] ? [U2] extends [never] ? [Set>, Set>] : [Set>, Set>, Set>] : [ Set>, Set>, Set>, Set> ] export type GuardMapPartition< K, V, U1, U2 = never, U3 = never > = [U3] extends [never] ? [U2] extends [never] ? [Map>, Map>] : [ Map>, Map>, Map> ] : [ Map>, Map>, Map>, Map> ] /** `[pattern, value]` pair used by binary-array / binary-pojo forms. */ export type BinaryPair = readonly [unknown, V] /** * Value types from an array of binary pairs. * @example UnwrappedBinaryArrayValues<[[P, string], [P, number]]> → string | number */ export type UnwrappedBinaryArrayValues = T[number] extends BinaryPair ? V : never /** * Pojo of binary pairs → pojo of unwrapped values (same keys). * @example UnwrappedBinaryPojo<{ a: [P, number]; b: [P, string] }> → { a: number; b: string } */ export type UnwrappedBinaryPojo = { [K in keyof T]: T[K] extends BinaryPair ? V : never } /** True when every property value is a binary `[pattern, value]` pair. */ export type IsBinaryPojo = { [K in keyof T]-?: T[K] extends BinaryPair ? true : false }[keyof T] extends true ? true : false /** * Map `siftr(...args)` application onto a pojo. * - `siftr()(pojo)` → passthru, or binary unwrap when all values are pairs * - `siftr(predicate | schema)(pojo)` → two buckets * - `siftr([p1, p2, …])(pojo)` → N+1 buckets */ export type PojoSiftrResult< T extends object, P extends readonly unknown[] > = P extends readonly [] ? IsBinaryPojo extends true ? [ PojoBucket>, PojoBucket> ] : [PojoBucket, T] : P extends readonly [infer Only] ? Only extends readonly unknown[] ? PojoSiftResult : Only extends TypeGuard ? [ { [K in keyof T]?: Extract }, { [K in keyof T]?: Exclude } ] : Only extends object ? [PojoSchemaMatchBucket, PojoBucket] : [PojoBucket, PojoBucket] : unknown[] /** * @example * sift(input, optionalSchemaOrPattern) * * // Working with plain-objects (pojos) * 1: sift({ key: [pattern, value], ... }) * 2: sift({ key: value }, { key: pattern }) * 3: sift({ key: value }, pattern) * 4: sift({ key: value }, [pattern, pattern, ...]) * * // Working with arrays * 5: sift([[pattern, value], [pattern, value], ...]) * 6: sift([value, value], [pattern, pattern]) * 7: sift([value, value], value-pattern) * 8: sift([value, value], ...value-patterns) */ // ── Primitives (before arrays: string is ArrayLike and can confuse generics) ─ /** Primitive passthru target types. */ export type Primitive = | string | number | boolean | symbol | bigint | null | undefined /** * Primitive passthru (no schema). * @example sift('string') // [undefined, 'string'] * @example sift(1) // [undefined, 1] */ export function sift(input: T): [undefined, T] // ── Arrays (must stay before other collections) ─────────────────────────── /** * Binary array form: each entry is `[pattern, value]`. * Returns two buckets of the unwrapped values (match / remainder). * @example sift([[isString, 'a'], [isString, 1]]) // [string[], number[]] union buckets */ export function sift( input: T ): [ UnwrappedBinaryArrayValues[], UnwrappedBinaryArrayValues[] ] /** * Array + type guard: match bucket is `Extract`; rest is `Exclude`. * @example sift(['a', 1] as Array, isString) // [string[], number[]] */ export function sift( input: readonly T[], guard: TypeGuard ): [Array>, Array>] /** * Array + two type guards: each match arm narrows; remainder is Exclude of both. * @example sift(items, isString, isNumber) // [string[], number[], boolean[]] */ export function sift( input: readonly T[], g1: TypeGuard, g2: TypeGuard ): [ Array>, Array>, Array> ] /** * Array + three type guards: three match arms + exhaustive remainder. */ export function sift( input: readonly T[], g1: TypeGuard, g2: TypeGuard, g3: TypeGuard ): [ Array>, Array>, Array>, Array> ] /** Array + four type guards. */ export function sift( input: readonly T[], g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard ): [ Array>, Array>, Array>, Array>, Array> ] /** Array + five type guards. */ export function sift( input: readonly T[], g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard, g5: TypeGuard ): [ Array>, Array>, Array>, Array>, Array>, Array> ] /** * Array + positional schema (one pattern per index). * Always two buckets (match / remainder) — not N+1 rest-pattern buckets. * Match elements use {@link MatchPayload} so schema slots may `pluck`. * @example sift(rows, [{ age: pluck(isNumber) }, isString, …]) */ export function sift( input: readonly T[], schema: S ): [Array>, T[]] /** * Array input with rest patterns: element type preserved; * bucket count = patterns + remainder. * @example sift(items, patternA, patternB) // three buckets (no type-guard narrowing) */ export function sift( input: readonly T[], ...patterns: P ): ArraySiftResult // ── Set / Map / Iterable (before pojo: they are objects) ─────────────────── /** * Set + type guard: match / remainder Sets with narrowed element types. */ export function sift( input: ReadonlySet, guard: TypeGuard ): [Set>, Set>] /** Set + two type guards. */ export function sift( input: ReadonlySet, g1: TypeGuard, g2: TypeGuard ): [Set>, Set>, Set>] /** Set + three type guards. */ export function sift( input: ReadonlySet, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard ): [ Set>, Set>, Set>, Set> ] /** Set + four type guards. */ export function sift( input: ReadonlySet, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard ): [ Set>, Set>, Set>, Set>, Set> ] /** Set + five type guards. */ export function sift( input: ReadonlySet, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard, g5: TypeGuard ): [ Set>, Set>, Set>, Set>, Set>, Set> ] /** * Set input: element type preserved; each bucket is a `Set`. * @example sift(new Set([1, 'a']), isNumber) // [Set, Set<…>] */ export function sift( input: ReadonlySet, ...patterns: P ): SetSiftResult /** * Map + type guard on values: match / remainder Maps with narrowed `V`. */ export function sift( input: ReadonlyMap, guard: TypeGuard ): [Map>, Map>] /** Map + two type guards on values. */ export function sift( input: ReadonlyMap, g1: TypeGuard, g2: TypeGuard ): [ Map>, Map>, Map> ] /** Map + three type guards on values. */ export function sift( input: ReadonlyMap, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard ): [ Map>, Map>, Map>, Map> ] /** Map + four type guards on values. */ export function sift( input: ReadonlyMap, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard ): [ Map>, Map>, Map>, Map>, Map> ] /** Map + five type guards on values. */ export function sift( input: ReadonlyMap, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard, g5: TypeGuard ): [ Map>, Map>, Map>, Map>, Map>, Map> ] /** * Map input: patterns match **values**; keys + value types preserved. * @example sift(new Map([[0, 'a'], [1, 1]]), isString) */ export function sift( input: ReadonlyMap, ...patterns: P ): MapSiftResult /** * IterableIterator + type guard: yielded values narrowed in array buckets. */ export function sift( input: IterableIterator, guard: TypeGuard ): [Array>, Array>] /** IterableIterator + two type guards. */ export function sift( input: IterableIterator, g1: TypeGuard, g2: TypeGuard ): [ Array>, Array>, Array> ] /** IterableIterator + three type guards. */ export function sift( input: IterableIterator, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard ): [ Array>, Array>, Array>, Array> ] /** IterableIterator + four type guards. */ export function sift( input: IterableIterator, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard ): [ Array>, Array>, Array>, Array>, Array> ] /** IterableIterator + five type guards. */ export function sift( input: IterableIterator, g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard, g5: TypeGuard ): [ Array>, Array>, Array>, Array>, Array>, Array> ] /** * IterableIterator (generators, etc.): yielded values collected into arrays. * Requires patterns at runtime (same as implementation). * @example sift(generator(), patternA, patternB) */ export function sift( input: IterableIterator, ...patterns: P ): IterableSiftResult // ── Pojo ────────────────────────────────────────────────────────────────── /** * Binary pojo form: each property is `[pattern, value]`. * Returns two partials of the unwrapped value types. * @example sift({ title: [isString, 'header'], slug: [isString, 1] }) */ export function sift< const T extends { [K in keyof T]: BinaryPair } >( input: T ): [ PojoBucket>, PojoBucket> ] /** * Pojo + array of value-patterns → N match buckets + remainder. * Match values use {@link MatchPayload} (pluck-aware). * @example sift({ a: 1, b: 'x' }, [isNumber, isString]) */ export function sift( input: T, patterns: readonly [...P] ): PojoSiftResult /** * Pojo + type guard on values: per-key Extract / Exclude in partial buckets. * @example sift(data, isString) */ export function sift( input: T, guard: TypeGuard ): [ { [K in keyof T]?: Extract }, { [K in keyof T]?: Exclude } ] /** * Pojo + value predicate applied to every entry → match / remainder. * @example sift(data, (v) => typeof v === 'string') */ export function sift( input: T, predicate: (value: T[keyof T]) => unknown ): [PojoBucket, PojoBucket] /** * Pojo + per-key schema object → match / remainder partials. * Schema slots may `pluck`; match values use {@link MatchPayload}. * Placed after function overloads so predicates are not treated as schemas. * @example sift(data, { title: isString, age: pluck(isNumber) }) */ export function sift< T extends object, const S extends Record >( input: T, schema: S ): [PojoSchemaMatchBucket, PojoBucket] /** * Pojo passthru (no schema): empty matches + full input. * @example sift({ a: 1 }) // [{}, { a: 1 }] */ export function sift(input: T): [PojoBucket, T] // ── Fallback ────────────────────────────────────────────────────────────── /** Other untyped inputs (plain iterators without patterns, …). */ export function sift( input: unknown, ...optionalSchemaOrPattern: unknown[] ): unknown[] /** * Curried form of `sift`: fix the schema first, apply to input later. * * @example * const partition = siftr(isString, isNumber) * partition(['a', 1, 'b']) // [['a', 'b'], [1], []] * * const byType = siftr([isString, isNumber]) * byType({ a: 1, b: 'x' }) // partial pojo buckets */ /** Single type guard: narrow match bucket via Extract; remainder via Exclude. */ export function siftr( guard: TypeGuard ): { (input: readonly T[]): [Array>, Array>] (input: ReadonlySet): [Set>, Set>] ( input: ReadonlyMap ): [Map>, Map>] ( input: IterableIterator ): [Array>, Array>] ( input: O ): [ { [K in keyof O]?: Extract }, { [K in keyof O]?: Exclude } ] } /** Two type guards: match arms + remainder Exclude of both. */ export function siftr( g1: TypeGuard, g2: TypeGuard ): { ( input: readonly T[] ): [ Array>, Array>, Array> ] ( input: ReadonlySet ): [Set>, Set>, Set>] ( input: ReadonlyMap ): [ Map>, Map>, Map> ] ( input: IterableIterator ): [ Array>, Array>, Array> ] } /** Three type guards: three match arms + exhaustive remainder. */ export function siftr( g1: TypeGuard, g2: TypeGuard, g3: TypeGuard ): { ( input: readonly T[] ): [ Array>, Array>, Array>, Array> ] ( input: ReadonlySet ): [ Set>, Set>, Set>, Set> ] ( input: ReadonlyMap ): [ Map>, Map>, Map>, Map> ] ( input: IterableIterator ): [ Array>, Array>, Array>, Array> ] } /** Four type guards. */ export function siftr( g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard ): { ( input: readonly T[] ): [ Array>, Array>, Array>, Array>, Array> ] ( input: ReadonlySet ): [ Set>, Set>, Set>, Set>, Set> ] ( input: ReadonlyMap ): [ Map>, Map>, Map>, Map>, Map> ] ( input: IterableIterator ): [ Array>, Array>, Array>, Array>, Array> ] } /** Five type guards. */ export function siftr( g1: TypeGuard, g2: TypeGuard, g3: TypeGuard, g4: TypeGuard, g5: TypeGuard ): { ( input: readonly T[] ): [ Array>, Array>, Array>, Array>, Array>, Array> ] ( input: ReadonlySet ): [ Set>, Set>, Set>, Set>, Set>, Set> ] ( input: ReadonlyMap ): [ Map>, Map>, Map>, Map>, Map>, Map> ] ( input: IterableIterator ): [ Array>, Array>, Array>, Array>, Array>, Array> ] } export function siftr( ...patterns: P ): { ( input: T ): P extends readonly [] ? [ UnwrappedBinaryArrayValues[], UnwrappedBinaryArrayValues[] ] : ArraySiftResult, P> ( input: readonly T[] ): P extends readonly [readonly unknown[]] ? [Array>, T[]] // positional schema (+ pluck) : ArraySiftResult (input: ReadonlySet): SetSiftResult (input: ReadonlyMap): MapSiftResult (input: IterableIterator): IterableSiftResult (input: T): PojoSiftrResult ( input: T ): P extends readonly [] ? [undefined, T] : unknown[] (input: unknown): unknown[] } /** * Build a matcher suitable for `Array#filter` / `Array#map`. * On match returns the value (or a pluck transform at runtime); otherwise `undefined`. * * @example * items.filter(byPattern({ age: 36 })) * items.map(byPattern({ age: pluck(isNumber) })) // number | undefined */ export function byPattern( pattern: (value: T) => value is U ): (input: T) => U | undefined /** * Structural / pluck patterns: element type inferred at the call site * (e.g. from `array.map` / `array.filter`). Match result is {@link MatchPayload}. * @example users.map(byPattern({ age: pluck(isNumber) })) */ export function byPattern( pattern: P ): (input: T) => MatchPayload | undefined /** * Explicit element type (e.g. `byPattern<User>({ age: 36 })` for filter). * Prefer the overload above when using `pluck` so the payload type is preserved. */ export function byPattern( pattern: unknown ): (input: T) => T | undefined