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