Skip to content

Commit

Permalink
feat(exhaustive): Add doc
Browse files Browse the repository at this point in the history
  • Loading branch information
gvergnaud committed Jun 10, 2024
1 parent 25e74be commit 1585b35
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 6 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -561,12 +561,36 @@ match(...)
.exhaustive()
```

Runs the pattern-matching expression and returns its result. It also enables exhaustiveness checking, making sure at compile time that we have handled all possible cases.
Runs the pattern-matching expression and returns its result. It also enables exhaustiveness checking, making sure that we have handled all possible cases **at compile time**.

By default, `.exhaustive()` will throw an error if the input value wasn't handled by any `.with(...)` clause. This should only happen if your types are incorrect.

It is possible to pass your own handler function as a parameter to decide what should happen if an unexpected value has been received. You can for example throw your own custom error:

```ts
match(...)
.with(...)
.exhaustive((unexpected: unknown) => {
throw MyCustomError(unexpected);
})
```

Or log an error and return a default value:

```ts
match<string, number>(...)
.with(P.string, (str) => str.length)
.exhaustive((notAString: unknown) => {
console.log(`received an unexpected value: ${notAString}`);
return 0;
})
```

#### Signature

```ts
function exhaustive(): TOutput;
function exhaustive(handler: (unexpectedValue: unknown) => TOutput): TOutput;
```

#### Example
Expand Down
4 changes: 2 additions & 2 deletions src/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,9 @@ class MatchExpression<input, output> {
return handler(this.input);
}

exhaustive(catcher = defaultCatcher): output {
exhaustive(unexpectedValueHandler = defaultCatcher): output {
if (this.state.matched) return this.state.value;
return catcher(this.input);
return unexpectedValueHandler(this.input);
}

run(): output {
Expand Down
2 changes: 1 addition & 1 deletion src/types/Match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,6 @@ type MakeTuples<ps extends readonly any[], value> = {
type Exhaustive<output, inferredOutput> = {
(): PickReturnValue<output, inferredOutput>;
<otherOutput>(
handler: (unmatchedValue: unknown) => PickReturnValue<output, otherOutput>
handler: (unexpectedValue: unknown) => PickReturnValue<output, otherOutput>
): PickReturnValue<output, Union<inferredOutput, otherOutput>>;
};
6 changes: 4 additions & 2 deletions tests/exhaustive-fallback.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ describe('Exhaustive fallback function', () => {
.with('b', (x) => x)
.exhaustive((v) => ({ unexpectedValue: v }));

// check return type
type t = Expect<
Equal<typeof result, { unexpectedValue: unknown } | 'a' | 'b'>
>;
Expand All @@ -33,7 +32,10 @@ describe('Exhaustive fallback function', () => {
.with('a', (x) => x)
.with('b', (x) => x)
// @ts-expect-error 'c' isn't assignable to a|b
.exhaustive(() => 'c');
.exhaustive(() => {
// Note: ideally the error message should be here.
return 'c';
});
});

it('should return a value assignable .returnType<T>()', () => {
Expand Down

0 comments on commit 1585b35

Please sign in to comment.