Skip to content

Instantly share code, notes, and snippets.

@Lucifier129
Last active February 5, 2022 14:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lucifier129/8bbf8e3f1c780dab8f1071b47aca1dd3 to your computer and use it in GitHub Desktop.
Save Lucifier129/8bbf8e3f1c780dab8f1071b47aca1dd3 to your computer and use it in GitHub Desktop.
// library
type Handler<T, R> = [new (...args: any) => T, (input: T) => R];
type HandlerPattern<T extends Handler<any, any>> = T extends Handler<
infer Pattern,
any
>
? Pattern
: unknown;
type HandlerOutput<T extends Handler<any, any>> = T extends Handler<
any,
infer Output
>
? Output
: unknown;
type Handlers<T> = Handler<any, T>[];
type HandlersOutput<T extends Handlers<any>> = T extends Handlers<infer R>
? R
: unknown;
type UniquePattern<T, HS extends Handlers<any>> = T extends HandlerPattern<
HS[number]
>
? never
: T;
class Match<T extends Handlers<any>> {
input: unknown;
patterns: T;
constructor(input: unknown, patterns: T) {
this.input = input;
this.patterns = patterns;
}
case<U, R extends HandlersOutput<T>>(
Ctor: new (...args: any) => UniquePattern<U, T>,
handler: (data: U) => R
): Match<[...T, Handler<U, R>]> {
return new Match(this.input, [...this.patterns, [Ctor, handler]]);
}
default<R extends HandlersOutput<T>>(
handler: (input: unknown) => R
): HandlersOutput<T> {
for (const [Ctor, handler] of this.patterns) {
if (this.input instanceof Ctor) {
return handler(this.input);
}
}
return handler(this.input);
}
}
const match = (input: unknown): Match<[]> => {
return new Match(input, []);
};
// email example
abstract class BaseEmail {
value: string;
constructor(email: string) {
this.value = email;
}
getEmail() {
return this.value;
}
}
class ValidEmail extends BaseEmail {
getValidEmail() {
return this.getEmail();
}
}
class InvalidEmail extends BaseEmail {
getInvalidEmail() {
return this.getEmail();
}
}
class AnotherEmail extends BaseEmail {
getAnotherEmail() {
return this.getEmail();
}
}
const createEmail = (inputEmail: string): BaseEmail => {
if (inputEmail.includes('@')) {
return new ValidEmail(inputEmail);
}
return new InvalidEmail(inputEmail);
};
const handleValidEmail = (email: ValidEmail) => {
return `valid email ${email.getValidEmail()}`;
};
const handleInvalidEmail = (email: InvalidEmail) => {
return `invalid email ${email.getInvalidEmail()}`;
};
const email0 = createEmail('abc');
const email1 = createEmail('abc@example');
const email2 = new AnotherEmail('another@example');
const result0 = match(email0)
.case(ValidEmail, handleValidEmail)
.case(InvalidEmail, handleInvalidEmail)
.default(() => 'default 0');
const result1 = match(email1)
.case(ValidEmail, handleValidEmail)
.case(InvalidEmail, handleInvalidEmail)
.default(() => 'default 1');
const result2 = match(email2)
.case(ValidEmail, handleValidEmail)
.case(InvalidEmail, handleInvalidEmail)
.default(() => 'default 2');
console.log({
result0,
result1,
result2,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment