[RFC] runtime match with type
Feature Request
typia has been used so far to verify types or to give random values.
And now I think I can manage the work after the verification, not the verification.
ts-pattern and pattycake resemble a pattern match of rust or c#, for which we take the pattern as a function factor value. In the case of typia, you can take the type as a factor and convert it into an if statement in advance at compilation time.
You just have to write the verification syntax into a type and give it to the user how to handle the verified type
How to use?
just write match
type ValidateMatchFunction<T, F> = F extends (arg: infer P) => any
? P extends T
? F
: never
: never;
function match<
const T,
MatchFns extends ((arg: any) => any)[],
ReturnTypes extends MatchFns[number] extends (...args: any[]) => infer R
? R
: never,
>(
input: T,
...fns: {
[K in keyof MatchFns]: ValidateMatchFunction<T, MatchFns[K]>;
}
): ReturnTypes | undefined;
const result = match(1 as 1 | 2 | 3 | 4,
(src: 1) => "one",
(src: 2 | 3) => "two or three"
);
result;
// ^? const result: "one" | "two or three" | undefined
const result2 = matchAssert(4 as 1 | 2 | 3 | 4,
(src: 1) => "one",
(src: 2 | 3) => "two or three"
);
// expected assertsion error!
After compilation, it changes to a native conditional statement.
Similar library?
-
Effect.match
import { Match } from "effect" // Create a Matcher for objects with properties 'a' or 'b' // // ┌─── (u: { a: number; } | { b: string; }) => string | number // ▼ const match = Match.type<{ a: number } | { b: string }>().pipe( // Match an object with a numeric property 'a' Match.when({ a: Match.number }, (_) => _.a), // Match an object with a string property 'b' Match.when({ b: Match.string }, (_) => _.b), // Ensure all cases are covered Match.exhaustive ) console.log(match({ a: 0 })) // Output: 0 console.log(match({ b: "hello" })) // Output: "hello" -
ts-pattern.match
import { match, P } from 'ts-pattern'; type Data = | { type: 'text'; content: string } | { type: 'img'; src: string }; type Result = | { type: 'ok'; data: Data } | { type: 'error'; error: Error }; const result: Result = ...; const html = match(result) .with({ type: 'error' }, () => <p>Oups! An error occured</p>) .with({ type: 'ok', data: { type: 'text' } }, (res) => <p>{res.data.content}</p>) .with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => <img src={src} />) .exhaustive(); -
pattycake.match
let html = match(result) .with( { type: 'error', error: { foo: [1, 2] }, nice: '' }, () => '<p>Oups! An error occured</p>', ) .with({ type: 'ok', data: { type: 'text' } }, function (data) { return '<p>420</p>'; }) .with( { type: 'ok', data: { type: 'img', src: 'hi' } }, (src) => `<img src=${src} />`, ) .otherwise(() => 'idk bro'); // compiled let html; out: { if ( result.type === 'error' && Array.isArray(result.error.foo) && result.error.foo.length >= 2 && result.error.foo[0] === 1 && result.error.foo[1] === 2 ) { html = '<p>Oups! An error occured</p>'; break out; } if (result.type === 'ok' && result.data.type === 'text') { let data = result; html = '<p>420</p>'; break out; } if ( result.type === 'ok' && result.data.type === 'img' && result.data.src === 'hi' ) { let src = result; html = `<img src=${src} />`; break out; } html = 'idk bro'; break out; }
Of the following, only patty-cake is changes native at compilation time.
but the form will be very different from typia.match.
This is because typia can use pre-declared types so that you don't have to rewrite the match case.
If I have a chance, I'd like to work on it. I don't know how long it will take
You can start from the v7.0 branch, and typia.functional module would be helpful.
Also, instead of making both matches and asserMatches functions at the same time, I think the last callback function should be the error handler. If the last function be skipped, it throws error.
If your contribution comes after v7 release, this feature would come in v7.1 minor update.
https://github.com/samchon/typia/tree/v7.0/src/programmers/functional
oh.. thanks! Are you talking about this interface?
const result = match(1 as 1 | 2 | 3 | 4,
(src: 1) => "one",
(src: 2 | 3) => "two or three",
(err: UnmatchedError) => e.toString()
);
result;
// ^? const result: "one" | "two or three" | string
const result2 = matchAssert(4 as 1 | 2 | 3 | 4,
(src: 1) => "one",
(src: 2 | 3) => "two or three"
);
// expected assertsion error!
Yes it is. I think IValidation.IFailure would be proper.
It's a feature that looks like a lot of fun! I will use this function very often if it is added. This feature is also larger than other libraries. ( But it can also confuse a lot of people when it's an object type. If there are people who can't distinguish subtypes. )
Close due to no one challanges.