Skip to content

Commit 9b27384

Browse files
committed
perf: runtime perf improvement in .with
1 parent 4b96fe1 commit 9b27384

1 file changed

Lines changed: 29 additions & 32 deletions

File tree

src/match.ts

Lines changed: 29 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ import { Match } from './types/Match';
33
import * as symbols from './internals/symbols';
44
import { matchPattern } from './internals/helpers';
55

6+
type MatchState<output> =
7+
| { matched: true; value: output }
8+
| { matched: false; value: undefined };
9+
10+
const unmatched: MatchState<never> = {
11+
matched: false,
12+
value: undefined,
13+
};
14+
615
/**
716
* `match` creates a **pattern matching expression**.
817
* * Use `.with(pattern, handler)` to pattern match on the input.
@@ -22,18 +31,9 @@ import { matchPattern } from './internals/helpers';
2231
export function match<const input, output = symbols.unset>(
2332
value: input
2433
): Match<input, output> {
25-
return new MatchExpression(value) as any;
34+
return new MatchExpression(value, unmatched) as any;
2635
}
2736

28-
type MatchState<output> =
29-
| { matched: true; value: output }
30-
| { matched: false; value: undefined };
31-
32-
const unmatched: MatchState<never> = {
33-
matched: false,
34-
value: undefined,
35-
};
36-
3737
/**
3838
* This class represents a match expression. It follows the
3939
* builder pattern, we chain methods to add features to the expression
@@ -44,10 +44,7 @@ const unmatched: MatchState<never> = {
4444
* can be found in src/types/Match.ts.
4545
*/
4646
class MatchExpression<input, output> {
47-
constructor(
48-
private input: input,
49-
private state: MatchState<output> = unmatched
50-
) {}
47+
constructor(private input: input, private state: MatchState<output>) {}
5148

5249
with(...args: any[]): MatchExpression<input, output> {
5350
if (this.state.matched) return this;
@@ -56,38 +53,38 @@ class MatchExpression<input, output> {
5653
args[args.length - 1];
5754

5855
const patterns: Pattern<input>[] = [args[0]];
59-
const predicates: ((value: input) => unknown)[] = [];
56+
let predicate: ((value: input) => unknown) | undefined = undefined;
6057

61-
// case with guard as second argument
6258
if (args.length === 3 && typeof args[1] === 'function') {
59+
// case with guard as second argument
6360
patterns.push(args[0]);
64-
predicates.push(args[1]);
61+
predicate = args[1];
6562
} else if (args.length > 2) {
6663
// case with several patterns
6764
patterns.push(...args.slice(1, args.length - 1));
6865
}
6966

67+
let hasSelections = false;
7068
let selected: Record<string, unknown> = {};
69+
const select = (key: string, value: unknown) => {
70+
hasSelections = true;
71+
selected[key] = value;
72+
};
7173

72-
const matched = Boolean(
73-
patterns.some((pattern) =>
74-
matchPattern(pattern, this.input, (key, value) => {
75-
selected[key] = value;
76-
})
77-
) && predicates.every((predicate) => predicate(this.input))
78-
);
74+
const matched =
75+
patterns.some((pattern) => matchPattern(pattern, this.input, select)) &&
76+
(predicate ? Boolean(predicate(this.input)) : true);
77+
78+
const selections = hasSelections
79+
? symbols.anonymousSelectKey in selected
80+
? selected[symbols.anonymousSelectKey]
81+
: selected
82+
: this.input;
7983

8084
const state = matched
8185
? {
8286
matched: true as const,
83-
value: handler(
84-
Object.keys(selected).length
85-
? symbols.anonymousSelectKey in selected
86-
? selected[symbols.anonymousSelectKey]
87-
: selected
88-
: this.input,
89-
this.input
90-
),
87+
value: handler(selections, this.input),
9188
}
9289
: unmatched;
9390

0 commit comments

Comments
 (0)