Skip to content

Instantly share code, notes, and snippets.

@freddi301
Created January 16, 2019 15:24
Show Gist options
  • Save freddi301/4f72baf3faad99bf250de842d854caaf to your computer and use it in GitHub Desktop.
Save freddi301/4f72baf3faad99bf250de842d854caaf to your computer and use it in GitHub Desktop.
TypeScript taggedUnion pattern match
class TaggedUnionValue<
TaggedUnion extends { [K in keyof TaggedUnion]: any[] },
Tag extends keyof TaggedUnion
> {
constructor(
private readonly tag: Tag,
private readonly value: TaggedUnion[Tag],
) {}
}
function match<
TaggedUnion extends { [K in keyof TaggedUnion]: any[] },
Tag extends keyof TaggedUnion
>({ tag, value }: TaggedUnionValue<TaggedUnion, Tag>) {
return <Result>(
cases: { [K in keyof TaggedUnion]: (...args: TaggedUnion[K]) => Result },
) => cases[tag](value);
}
function unionConstructor<
TaggedUnion extends { [K in keyof TaggedUnion]: any[] }
>() {
return <Tag extends keyof TaggedUnion>(
tag: Tag,
...value: TaggedUnion[Tag]
): Data<TaggedUnion> => new TaggedUnionValue(tag, value);
}
type Data<
TaggedUnion extends { [K in keyof TaggedUnion]: any[] }
> = TaggedUnionValue<TaggedUnion, keyof TaggedUnion>;
interface Maybe<T> {
Some: [T];
None: [];
}
const Maybe = <T>() => unionConstructor<Maybe<T>>();
const v = Maybe<number>()("Some", 77);
const u = match(v)({
Some: value => "hel",
None: () => "hello",
});
function gotSweets(sweets: Data<Maybe<number>>) {
return match(sweets)({
Some: count => count,
None: () => 10,
});
}
interface Email {
Valid: [string];
Invalid: [string];
Uncheck: [string];
}
const Email = unionConstructor<Email>();
const m = Email("Uncheck", "[email protected]");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment