export type OperatorMatcher<
TProperty extends string = any,
TPossibleTypes extends string = any,
TInput extends { [key in TProperty]: TPossibleTypes } = any
> = {
[Type in TPossibleTypes]: Operator<
Extract<TInput, { [key in TProperty]: Type }>,
any
>;
};
export const operatorMatch = <
TProperty extends string,
TPossibleTypes extends string,
TInput extends { [key in TProperty]: TPossibleTypes },
TMatcher extends OperatorMatcher<TProperty, TPossibleTypes, TInput>
>(
property: TProperty,
input: TInput,
matcher: TMatcher
): [
Extract<TInput, { [key in TProperty]: TInput[TProperty] }>,
TMatcher[TInput[TProperty]]
] => {
return [
input as Extract<TInput, { [key in TProperty]: TInput[TProperty] }>,
matcher[input[property]]
];
};
export const match = <
TProperty extends string,
TMatcher extends OperatorMatcher<TProperty>
>(
property: TProperty,
matcher: TMatcher
): Operator<TMatcher[TProperty], any> => {
return createOperator("match", property, (err, context, value, next) => {
if (err) return next(err, value);
const [newValue, operator] = operatorMatch(property, value, matcher);
next(null, newValue, {
path: {
name: newValue[property],
operator
}
});
});
};
Usage example:
export const matchReference = (
matcher: OperatorMatcher<
"mode",
"noReference" | "hasReference",
| {
mode: "noReference";
id: string;
referenceId: null;
}
| {
mode: "hasReference";
id: string;
referenceId: string;
}
>
) => match("mode", matcher);
export const setActiveToken = pipe(
o.matchReference({
noReference: o.enterNotFoundState(),
hasReference: pipe(
o.enterLoadingState(),
o.loadNoteById(),
o.matchHasRemoteReference({
notFound: o.enterNotFoundState(),
found: o.enterLoadedState()
})
)
})
);