@monstermann/match
Version:
Zero-runtime exhaustive pattern matching.
124 lines (123 loc) • 11.4 kB
text/typescript
import { ConditionalPick, IsEqual, IsLiteral, IsUnion, OptionalKeysOf, Primitive, RequireAtLeastOne, RequiredKeysOf, Simplify, UnionToTuple } from "type-fest";
//#region src/types/IsPlainObject.d.ts
type IsPlainObject<T> = T extends object ? T extends any[] ? false : T extends Function ? false : true : false;
//#endregion
//#region src/types/IsLiteral.d.ts
type IsLiteral$1<T> = IsLiteral<T> extends true ? true : T extends null | undefined ? true : false;
//#endregion
//#region src/types/LiteralFieldsOf.d.ts
type LiteralFieldsOf<T> = { [K in keyof T as IsLiteral$1<T[K]> extends true ? K : never]: T[K] };
//#endregion
//#region src/types/HasLiteralFields.d.ts
type HasLiteralFields<T> = [keyof LiteralFieldsOf<T>] extends [never] ? false : true;
//#endregion
//#region src/types/MatchShape.d.ts
type MatchShape<T, U> = IfTrue<{ [K in keyof U]: K extends keyof T ? U[K] extends T[K] ? true : false : false }[keyof U], T, never>;
type IfTrue<T, OnTrue, OnFalse> = true extends T ? OnTrue : OnFalse;
//#endregion
//#region src/types/DropShape.d.ts
type DropShape<T, U> = T extends unknown ? IsPlainObject<T> extends true ? [MatchShape<T, U>] extends [never] ? T : DropLiteralFields<T, U> : T : never;
type DropLiteralFields<T, U> = HasLiteralFields<U> extends true ? LiteralFieldsOf<U> extends infer V ? Exclude<T, V> : never : T;
//#endregion
//#region src/types/ExcludeMatchedShapes.d.ts
type ExcludeMatchedShapes<T, U> = [U] extends [never] ? T : U extends unknown ? IsEqual<T, U> extends true ? never : T : never;
//#endregion
//#region src/types/IsTrue.d.ts
type IsTrue<T> = true extends T ? true : false;
//#endregion
//#region src/types/HasShapes.d.ts
type HasShapes<T> = IsTrue<T extends unknown ? IsPlainObject<T> extends true ? true : false : false>;
//#endregion
//#region src/types/JoinMatchedShapes.d.ts
type JoinMatchedShapes<T, U> = HasLiteralFields<U> extends true ? T | U : T;
//#endregion
//#region src/types/MatchError.d.ts
interface MatchError<_i> {
__: never;
}
//#endregion
//#region src/types/PickShape.d.ts
type PickShape<T, U> = T extends unknown ? IsPlainObject<T> extends true ? Readonly<MatchShape<T, U> & U> : never : never;
//#endregion
//#region src/types/ShapePatterns.d.ts
type ShapePatterns<T> = T extends unknown ? T extends object ? AtLeastOneField<CleanOptionals<ConditionalPick<T, Primitive>>> : never : never;
type AtLeastOneField<T> = RequireAtLeastOne<T, keyof T>;
type CleanOptionals<T extends object> = Simplify<{ [K in RequiredKeysOf<T>]: T[K] } & { [K in OptionalKeysOf<T>]: T[K] | undefined }>;
//#endregion
//#region src/types/ShapeMatcherStrict.d.ts
interface ShapeMatcherStrict<Input, Output = never, Matches = never> {
returnType: MatchError<"Calling `.returnType<T>()` is only allowed directly after `match.shape(...)`">;
case: [Input] extends [never] ? MatchError<"Match is exhaustive"> : HasShapes<Input> extends true ? <const I extends ShapePatterns<Input>>(value: ExcludeMatchedShapes<I, Matches>, result: Output) => ShapeMatcherStrict<DropShape<Input, I>, Output, JoinMatchedShapes<Matches, I>> : MatchError<"No shapes left to match against">;
cond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input>(predicate: (value: Input) => value is I, result: Output) => ShapeMatcherStrict<Exclude<Input, I>, Output, Matches>) & ((predicate: (value: Input) => boolean, result: Output) => ShapeMatcherStrict<Input, Output, Matches>);
onCase: [Input] extends [never] ? MatchError<"Match is exhaustive"> : HasShapes<Input> extends true ? <const I extends ShapePatterns<Input>>(value: ExcludeMatchedShapes<I, Matches>, fn: (value: Simplify<PickShape<Input, I>>) => Output) => ShapeMatcherStrict<DropShape<Input, I>, Output, JoinMatchedShapes<Matches, I>> : MatchError<"No shapes left to match against">;
onCond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input>(predicate: (value: Input) => value is I, fn: (value: I) => Output) => ShapeMatcherStrict<Exclude<Input, I>, Output, Matches>) & ((predicate: (value: Input) => boolean, fn: (value: Input) => Output) => ShapeMatcherStrict<Input, Output, Matches>);
or: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (fallback: Output) => Output;
orElse: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (fallback: (value: Simplify<Readonly<Input>>) => Output) => Output;
orThrow: [Input] extends [never] ? () => Output : MatchError<Input>;
}
//#endregion
//#region src/types/Union.d.ts
type Union<A, B> = [B] extends [A] ? A : [A] extends [B] ? B : A | B;
//#endregion
//#region src/types/ShapeMatcher.d.ts
interface ShapeMatcher<Input, Output = never, Matches = never> {
case: [Input] extends [never] ? MatchError<"Match is exhaustive"> : HasShapes<Input> extends true ? <const I extends ShapePatterns<Input>, O>(value: ExcludeMatchedShapes<I, Matches>, result: O) => ShapeMatcher<DropShape<Input, I>, Union<Output, O>, JoinMatchedShapes<Matches, I>> : MatchError<"No shapes left to match against">;
cond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input, O>(predicate: (value: Input) => value is I, result: O) => ShapeMatcher<Exclude<Input, I>, Union<Output, O>, Matches>) & (<O>(predicate: (value: Input) => boolean, result: O) => ShapeMatcher<Input, Union<Output, O>, Matches>);
onCase: [Input] extends [never] ? MatchError<"Match is exhaustive"> : HasShapes<Input> extends true ? <const I extends ShapePatterns<Input>, O>(value: ExcludeMatchedShapes<I, Matches>, fn: (value: Simplify<PickShape<Input, I>>) => O) => ShapeMatcher<DropShape<Input, I>, Union<Output, O>, JoinMatchedShapes<Matches, I>> : MatchError<"No shapes left to match against">;
onCond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input, O>(predicate: (value: Input) => value is I, fn: (value: I) => O) => ShapeMatcher<Exclude<Input, I>, Union<Output, O>, Matches>) & (<O>(predicate: (value: Input) => boolean, fn: (value: Input) => O) => ShapeMatcher<Input, Union<Output, O>, Matches>);
or: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <O>(fallback: O) => Union<Output, O>;
orElse: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <O>(fallback: (value: Simplify<Readonly<Input>>) => O) => Union<Output, O>;
orThrow: [Input] extends [never] ? () => Output : MatchError<Input>;
returnType: [Output] extends [never] ? <O>() => ShapeMatcherStrict<Input, O> : MatchError<"Calling `.returnType<T>()` is only allowed directly after `match.shape(...)`">;
}
//#endregion
//#region src/types/ShapeVariations.d.ts
type ShapeVariations<T> = ExplodeValues<T, ExplodeKeys<T>>;
type ExplodeValues<T, K$1> = K$1 extends [infer Head, ...infer Tail] ? Head extends keyof T ? ExplodeValues<ExplodeValue<T, Head>, Tail> : never : T;
type ExplodeValue<T, K$1 extends keyof T, U = UnionToTuple<T[K$1]>, V$1 = never> = U extends [infer Head, ...infer Tail] ? ExplodeValue<T, K$1, Tail, V$1 | (T & Record<K$1, Head>)> : V$1;
type ExplodeKeys<T> = UnionToTuple<keyof { [K in keyof T as T[K] extends Primitive | null | undefined ? IsUnion<T[K]> extends true ? K : never : never]: true }>;
//#endregion
//#region src/types/DropValue.d.ts
type DropValue<T, U> = IsUnion<U> extends false ? IsLiteral$1<U> extends true ? Exclude<T, U> : T : T;
//#endregion
//#region src/types/ExcludeMatchedValues.d.ts
type ExcludeMatchedValues<T, U> = Exclude<T, U>;
//#endregion
//#region src/types/JoinMatchedValues.d.ts
type JoinMatchedValues<T, U> = IsLiteral$1<U> extends true ? T | U : T;
//#endregion
//#region src/types/PickValue.d.ts
type PickValue<T, U> = IsLiteral$1<U> extends true ? U : Extract<T, U>;
//#endregion
//#region src/types/ValueMatcherStrict.d.ts
interface ValueMatcherStrict<Input, Output = never, Matches = never> {
returnType: MatchError<"Calling `.returnType<T>()` is only allowed directly after `match(...)`">;
case: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <const I extends Input>(value: ExcludeMatchedValues<I, Matches>, result: Output) => ValueMatcherStrict<DropValue<Input, I>, Output, JoinMatchedValues<Matches, I>>;
cond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input>(predicate: (value: Input) => value is I, result: Output) => ValueMatcherStrict<Exclude<Input, I>, Output, Matches>) & ((predicate: (value: Input) => boolean, result: Output) => ValueMatcherStrict<Input, Output, Matches>);
onCase: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <const I extends Input>(value: ExcludeMatchedValues<I, Matches>, fn: (value: PickValue<Input, I>) => Output) => ValueMatcherStrict<DropValue<Input, I>, Output, JoinMatchedValues<Matches, I>>;
onCond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input>(predicate: (value: Input) => value is I, fn: (value: I) => Output) => ValueMatcherStrict<Exclude<Input, I>, Output, Matches>) & ((predicate: (value: Input) => boolean, fn: (value: Input) => Output) => ValueMatcherStrict<Input, Output, Matches>);
or: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (fallback: Output) => Output;
orElse: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (fallback: (value: Readonly<Input>) => Output) => Output;
orThrow: [Input] extends [never] ? () => Output : MatchError<Input>;
}
//#endregion
//#region src/types/ValueMatcher.d.ts
interface ValueMatcher<Input, Output = never, Matches = never> {
case: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <const I extends Input, O>(value: ExcludeMatchedValues<I, Matches>, result: O) => ValueMatcher<DropValue<Input, I>, Union<Output, O>, JoinMatchedValues<Matches, I>>;
cond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input, O>(predicate: (value: Input) => value is I, result: O) => ValueMatcher<Exclude<Input, I>, Union<Output, O>, Matches>) & (<O>(predicate: (value: Input) => boolean, result: O) => ValueMatcher<Input, Union<Output, O>, Matches>);
onCase: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <const I extends Input, O>(value: ExcludeMatchedValues<I, Matches>, fn: (value: PickValue<Input, I>) => O) => ValueMatcher<DropValue<Input, I>, Union<Output, O>, JoinMatchedValues<Matches, I>>;
onCond: [Input] extends [never] ? MatchError<"Match is exhaustive"> : (<const I extends Input, O>(predicate: (value: Input) => value is I, fn: (value: I) => O) => ValueMatcher<Exclude<Input, I>, Union<Output, O>, Matches>) & (<O>(predicate: (value: Input) => boolean, fn: (value: Input) => O) => ValueMatcher<Input, Union<Output, O>, Matches>);
or: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <O>(fallback: O) => Union<Output, O>;
orElse: [Input] extends [never] ? MatchError<"Match is exhaustive"> : <O>(fallback: (value: Readonly<Input>) => O) => Union<Output, O>;
orThrow: [Input] extends [never] ? () => Output : MatchError<Input>;
returnType: [Output] extends [never] ? <O>() => ValueMatcherStrict<Input, O> : MatchError<"Calling `.returnType<T>()` is only allowed directly after `match(...)`">;
}
//#endregion
//#region src/match.d.ts
interface Match {
<T extends Primitive | null | undefined>(value: T): ValueMatcher<T>;
shape: <T extends object>(value: IsPlainObject<T> extends true ? T : never) => ShapeMatcher<ShapeVariations<T>>;
}
declare const match: Match;
//#endregion
export { Match, match };