こんなtsファイルがあるとどう動くのか?というのを解説したい
import { match, P } from 'ts-pattern';
type Input = {
text: string
}
function validateInput(input: Input) {
return match(input.text)
.with(P.not(P.string.minLength(10)), () => [false, "textは10文字より大きくしてください。"])
.with(P.not(P.string.maxLength(100)), () => [false, "textは100文字より小さくしてください。"])
.with(P.not(P.string.regex(/^https?:\\/\\//)), () => [false, 'url形式にしてください。'])
.otherwise(() => [true, undefined]);
}
console.log(validateInput({
text: "<https://example.com/aaaaaaa>"
}))
動きとしては
match(input.text)は内部のMatchExpressionを返している
export function match<const input, output = symbols.unset>(
value: input
): Match<input, output> {
return new MatchExpression(value, unmatched) as any;
}
P.string.minLength(10) をconsole.logして実態を見てみると、[Symbol(@ts-pattern/matcher)]な関数などを持っているobjectになっている
{
[Symbol(@ts-pattern/matcher)]: [Function],
optional: [Function: optional],
and: [Function: and],
or: [Function: or],
select: [Function: select],
startsWith: [Function: startsWith],
endsWith: [Function: endsWith],
minLength: [Function: minLength],
maxLength: [Function: maxLength],
includes: [Function: includes],
regex: [Function: regex],
}
withの実装は以下
function with(...args: any[]): MatchExpression<input, output> {
if (this.state.matched) return this;
const handler: (selection: unknown, value: input) => output =
args[args.length - 1];
const patterns: Pattern<input>[] = [args[0]];
let predicate: ((value: input) => unknown) | undefined = undefined;
if (args.length === 3 && typeof args[1] === 'function') {
// case with guard as second argument
predicate = args[1];
} else if (args.length > 2) {
// case with several patterns
patterns.push(...args.slice(1, args.length - 1));
}
let hasSelections = false;
let selected: Record<string, unknown> = {};
const select = (key: string, value: unknown) => {
hasSelections = true;
selected[key] = value;
};
const matched =
patterns.some((pattern) => matchPattern(pattern, this.input, select)) &&
(predicate ? Boolean(predicate(this.input)) : true);
const selections = hasSelections
? symbols.anonymousSelectKey in selected
? selected[symbols.anonymousSelectKey]
: selected
: this.input;
const state = matched
? {
matched: true as const,
value: handler(selections, this.input),
}
: unmatched;
return new MatchExpression(this.input, state);
}