Skip to content

Commit

Permalink
feat(match): allow to match strictly
Browse files Browse the repository at this point in the history
  • Loading branch information
ecyrbe committed Aug 1, 2023
1 parent 3455aa2 commit c6183a1
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 25 deletions.
8 changes: 5 additions & 3 deletions src/internals/match/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ import * as Impl from "./impl/match";
* ```ts
* type Test<T> = Match<T, [
* With<{ msg: Match.arg0 }, S.Prepend<"Message: ">>,
* WithExact<number, string>,
* With<string, S.Append<": string">>,
* With<any, F.Constant<"default value">>
* ]>
* ```
*/
export type Match<
valueOrWithClauses = unset,
withClauses extends Impl.With<any, any>[] | unset | _ = unset
withClauses extends Impl.With<any, any, boolean>[] | unset | _ = unset
> = PartialApply<
MatchFn,
withClauses extends unset
? [unset, valueOrWithClauses]
: [valueOrWithClauses, withClauses]
? [unset, valueOrWithClauses]
: [valueOrWithClauses, withClauses]
>;

interface MatchFn extends Fn {
Expand All @@ -35,4 +36,5 @@ interface MatchFn extends Fn {

export namespace Match {
export type With<pattern, handler> = Impl.With<pattern, handler>;
export type WithExact<pattern, handler> = Impl.WithExact<pattern, handler>;
}
46 changes: 24 additions & 22 deletions src/internals/match/impl/match.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { arg, Call, Fn, PartialApply, unset } from "../../core/Core";
import { Primitive, UnionToIntersection } from "../../helpers";
import { Primitive, UnionToIntersection, Equal } from "../../helpers";

type GetWithDefault<Obj, K, Def> = K extends keyof Obj ? Obj[K] : Def;

Expand All @@ -18,30 +18,31 @@ type ReplaceArgsWithConstraint<pattern> = pattern extends arg<
? { [key in keyof pattern]: ReplaceArgsWithConstraint<pattern[key]> }
: pattern;

type DoesMatch<value, pattern> =
type DoesMatch<value, pattern, exact extends boolean> = exact extends true ?
Equal<value, pattern> :
value extends ReplaceArgsWithConstraint<pattern> ? true : false;

type ExtractArgObject<value, pattern> = pattern extends arg<infer N, any>
? { [K in N]: value }
: pattern extends []
? {}
: [value, pattern] extends [
[infer valueFirst, ...infer valueRest],
[infer patternFirst, ...infer patternRest]
]
[infer valueFirst, ...infer valueRest],
[infer patternFirst, ...infer patternRest]
]
? ExtractArgObject<valueRest, patternRest> &
ExtractArgObject<valueFirst, patternFirst>
ExtractArgObject<valueFirst, patternFirst>
: [value, pattern] extends [(infer valueFirst)[], (infer patternFirst)[]]
? ExtractArgObject<valueFirst, patternFirst>
: [value, pattern] extends [object, object]
? UnionToIntersection<
{
[k in keyof value & keyof pattern]: ExtractArgObject<
value[k],
pattern[k]
>;
}[keyof value & keyof pattern]
>
{
[k in keyof value & keyof pattern]: ExtractArgObject<
value[k],
pattern[k]
>;
}[keyof value & keyof pattern]
>
: {};

type WithDefaultArgs<Args extends any[], Def> = [Args[number]] extends [unset]
Expand All @@ -60,15 +61,16 @@ type ExtractArgs<value, pattern> = WithDefaultArgs<
[value]
>;

export type Match<value, patterns extends With<any, any>[]> = patterns extends [
With<infer pattern, infer handler>,
...infer restPatterns extends With<any, any>[]
export type Match<value, patterns extends With<any, any, boolean>[]> = patterns extends [
With<infer pattern, infer handler, infer exact>,
...infer restPatterns extends With<any, any, boolean>[]
]
? DoesMatch<value, pattern> extends true
? handler extends Fn
? Call<PartialApply<Extract<handler, Fn>, ExtractArgs<value, pattern>>>
: handler
: Match<value, restPatterns>
? DoesMatch<value, pattern, exact> extends true
? handler extends Fn
? Call<PartialApply<Extract<handler, Fn>, ExtractArgs<value, pattern>>>
: handler
: Match<value, restPatterns>
: never;

export type With<pattern, handler> = { pattern: pattern; handler: handler };
export type With<pattern, handler, exact extends boolean = false> = { exact: exact; pattern: pattern; handler: handler };
export type WithExact<pattern, handler> = With<pattern, handler, true>;
32 changes: 32 additions & 0 deletions test/match.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,38 @@ describe("Match", () => {
type test3 = Expect<Equal<res3, "ab">>;
});

it("should match with exact types", () => {
type MatchTest<T> = Call<
Match<
T,
[
Match.WithExact<{ msg: string }, Constant<"string">>,
Match.WithExact<{ msg: string; x: number }, Constant<"string + x">>,
Match.WithExact<{ msg: string; x: number; y: boolean }, Constant<"all">>,
Match.With<any, Constant<"don't match strictly with the provided types">>
]
>
>;

type res1 = MatchTest<{ msg: string }>;
// ^?
type test1 = Expect<Equal<res1, "string">>;

type res2 = MatchTest<{ msg: string; x: number }>;
// ^?
type test2 = Expect<Equal<res2, "string + x">>;

type res3 = MatchTest<{ msg: string; x: number; y: boolean }>;
// ^?
type test3 = Expect<Equal<res3, "all">>;

type res4 = MatchTest<{ msg: "hello" }>;
// ^?
type test4 = Expect<Equal<res4, "don't match strictly with the provided types">>;


});

it("Handlers can also be regular values", () => {
type MatchTest<T> = Call<
Match<
Expand Down

0 comments on commit c6183a1

Please sign in to comment.