diff --git a/src/index.ts b/src/index.ts index fcff87ae..724a7947 100644 --- a/src/index.ts +++ b/src/index.ts @@ -132,7 +132,7 @@ const builder = ( * that **all cases are handled**. `when` predicates * aren't supported on exhaustive matches. **/ - exhaustive: (): ExhaustiveMatch, b> => + exhaustive: (): ExhaustiveMatch, a, b> => builder(value, patterns) as any, }); diff --git a/src/types/Match.ts b/src/types/Match.ts index b5d9c8ba..85f8be54 100644 --- a/src/types/Match.ts +++ b/src/types/Match.ts @@ -116,7 +116,7 @@ export type EmptyMatch = Match & { * that **all cases are handled**. `when` predicates * aren't supported on exhaustive matches. **/ - exhaustive: () => ExhaustiveMatch, o>; + exhaustive: () => ExhaustiveMatch, i, o>; }; type NonExhaustivePattern = { __nonExhaustive: never } & i; @@ -125,7 +125,7 @@ type NonExhaustivePattern = { __nonExhaustive: never } & i; * ### ExhaustiveMatch * An interface to create an exhaustive pattern matching clause. */ -export type ExhaustiveMatch = { +export type ExhaustiveMatch = { /** * ### Match.with * If the data matches the pattern provided as first argument, @@ -138,7 +138,12 @@ export type ExhaustiveMatch = { selections: ExtractSelections ) => PickReturnValue ): ExhaustiveMatch< - Exclude>>, + // For performances, we pass both the original input and + // the distributedInput to ExhaustiveMatch, so we can compute the pattern + // from the original input, which is much faster than computing it + // from the distributed one. + Exclude>>, + i, PickReturnValue >; @@ -159,5 +164,7 @@ export type ExhaustiveMatch = { * every cases, and you should probably add a another `.with(...)` clause * to prevent potential runtime errors. * */ - run: [i] extends [never] ? () => o : NonExhaustivePattern; + run: [distributedInput] extends [never] + ? () => o + : NonExhaustivePattern; }; diff --git a/tests/exhaustive-match.test.ts b/tests/exhaustive-match.test.ts index 9e1e514f..747fdf36 100644 --- a/tests/exhaustive-match.test.ts +++ b/tests/exhaustive-match.test.ts @@ -45,9 +45,7 @@ describe('exhaustive()', () => { .exhaustive() .with('a', (x) => 1) .with('b', (x) => 1) - // twice the same match.exhaustive() // @ts-expect-error - .with('b', (x) => 1) .run(); match(input) @@ -97,9 +95,7 @@ describe('exhaustive()', () => { .exhaustive() .with(1, (x) => 1) .with(2, () => 3) - // twice the same match.exhaustive() // @ts-expect-error - .with(2, () => 3) .run(); match(input) @@ -515,12 +511,35 @@ describe('exhaustive()', () => { }); it('should work with generics', () => { - const f = (xs: a[]) => + const last = (xs: a[]) => match>(xs) .exhaustive() .with([], () => none) .with(__, (xs) => some(xs[xs.length - 1])) .run(); + + expect(last([1, 2, 3])).toEqual(some(3)); + }); + + it('should work with inputs of varying shapes', () => { + type Input = { type: 'test' } | ['hello', Option] | 'hello'[]; + type Output = ['hello', Option]; + const input = { type: 'test' } as Input; + + const output = match(input) + .exhaustive() + .with( + ['hello', { kind: 'some' }], + (x): Output => { + return x; + } + ) + .with(['hello'], (x) => { + return ['hello', none]; + }) + .with({ type: __ }, () => ['hello', none]) + .with([__], () => ['hello', none]) + .run(); }); }); });