Skip to content

Commit

Permalink
matcher: simplify matcher protocol and mark unstable
Browse files Browse the repository at this point in the history
  • Loading branch information
gvergnaud committed May 9, 2023
1 parent 2b16069 commit 0b92728
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 32 deletions.
55 changes: 42 additions & 13 deletions src/patterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,51 @@ import {
CustomP,
} from './types/Pattern';

export { Pattern };
export { 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.
Expand Down Expand Up @@ -682,15 +723,3 @@ export function typed<input>(): {
when: when as any,
};
}

export type Matchable<
narrowFn extends Fn,
input = unknown,
pattern = never
> = CustomP<input, pattern, narrowFn>;

export type Matcher<
narrowFn extends Fn,
input = unknown,
pattern = never
> = ReturnType<CustomP<input, pattern, narrowFn>[matcher]>;
12 changes: 7 additions & 5 deletions src/types/InvertPattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ type InvertPatternInternal<p, input> = 0 extends 1 & p
infer subpattern,
infer matcherType,
any,
infer narrowFn
infer narrowedOrFn
>
? {
not: DeepExclude<input, InvertPatternInternal<subpattern, input>>;
Expand All @@ -133,7 +133,9 @@ type InvertPatternInternal<p, input> = 0 extends 1 & p
and: ReduceIntersection<Extract<subpattern, readonly any[]>, input>;
or: ReduceUnion<Extract<subpattern, readonly any[]>, input>;
default: [subpattern] extends [never] ? input : subpattern;
custom: Override<narrowFn extends Fn ? Apply<narrowFn, input> : never>;
custom: Override<
narrowedOrFn extends Fn ? Apply<narrowedOrFn, input> : narrowedOrFn
>;
}[matcherType]
: p extends Primitives
? p
Expand Down Expand Up @@ -341,9 +343,9 @@ type InvertPatternForExcludeInternal<p, i, empty = never> =
InvertPatternForExcludeInternal<subpattern, i>
>;
default: excluded;
custom: excluded extends infer narrowFn extends Fn
? Apply<narrowFn, i>
: never;
custom: excluded extends infer narrowedOrFn extends Fn
? Apply<narrowedOrFn, i>
: excluded;
}[matcherType]
: p extends readonly any[]
? Extract<i, readonly any[]> extends infer arrayInput
Expand Down
4 changes: 2 additions & 2 deletions src/types/Pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ export type AnyMatcher = Matcher<any, any, any, any, any>;

type UnknownMatcher = Matcher<unknown, unknown, any, any>;

export type CustomP<input, pattern, narrowFn extends Fn> = Matcher<
export type CustomP<input, pattern, narrowedOrFn> = Matcher<
input,
pattern,
// 馃憜
// for the input type to be instantiated correctly
// on subpatterns, it has to be passed through.
'custom',
None,
narrowFn
narrowedOrFn
>;

export type ArrayP<input, p> = Matcher<input, p, 'array'>;
Expand Down
21 changes: 9 additions & 12 deletions tests/matcher-protocol.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import { isMatching, match, P } from '../src';
import { Equal, Expect, Fn } from '../src/types/helpers';
import { UnknownPattern } from '../src/types/Pattern';
import { Equal, Expect } from '../src/types/helpers';

describe('matcher protocol', () => {
type SomeValue<T> = T extends Some<infer V> ? V : never;

interface SomeNarrowFn<p extends P.Pattern<unknown> = never> extends Fn {
output: [p] extends [never]
? Some<SomeValue<this['input']>>
: Some<P.narrow<SomeValue<this['input']>, p>>;
interface SomeNarrowFn extends P.unstable_Fn {
output: Some<SomeValue<this['input']>>;
}

class Some<const T> {
constructor(public value: T) {}

static [P.matcher](): P.Matcher<SomeNarrowFn> {
static [P.matcher](): P.unstable_Matcher<SomeNarrowFn> {
return {
match: (input) => {
return {
Expand All @@ -24,7 +21,9 @@ describe('matcher protocol', () => {
};
}

[P.matcher](): P.Matcher<SomeNarrowFn<Extract<T, UnknownPattern>>> {
[P.matcher](): P.unstable_Matcher<
Some<T extends P.Pattern<unknown> ? P.infer<T> : T>
> {
return {
match: (input) => {
return {
Expand All @@ -35,15 +34,13 @@ describe('matcher protocol', () => {
};
}
}
interface NoneNarrowFn extends Fn {
output: None;
}

class None {
coucou: number;
constructor() {
this.coucou = 1;
}
static [P.matcher](): P.Matcher<NoneNarrowFn> {
static [P.matcher](): P.unstable_Matcher<None> {
return {
match: (input) => {
return { matched: input instanceof None };
Expand Down

0 comments on commit 0b92728

Please sign in to comment.