Skip to content

Instantly share code, notes, and snippets.

@motss
Last active July 31, 2024 14:27
Show Gist options
  • Save motss/76287e28079b4211de3af8e4565fd89d to your computer and use it in GitHub Desktop.
Save motss/76287e28079b4211de3af8e4565fd89d to your computer and use it in GitHub Desktop.
Simple implementation of Option, Some, None, and match() in TypeScript
type Some<T> = { some: T, none: false };
type None = { some: undefined, none: true };
type Option<T = unknown> = Some<T> | None;
type InferredSome<T extends Option> = T extends Some<infer R> ?
R :
never;
type OptionResult<T = unknown> = T extends null | undefined ? None : Some<T>;
function option<T>(value: T): OptionResult<T> {
if (value != null) {
return some(value) as OptionResult<T>;
}
return none() as OptionResult<T>;
}
function none(): None {
return { some: undefined, none: true };
}
function some<T>(value: T): Some<T> {
return { some: value, none: false };
}
class Matcher<T extends Option> {
#data!: T;
constructor(data: T) {
this.#data = data;
}
static new<U extends Option>(data: U) {
return new Matcher(data);
}
whenSome(cb: (value: InferredSome<T>) => void): Omit<Matcher<T>, 'whenSome'> {
cb(this.#data as InferredSome<T>);
return this;
}
whenNone(cb: () => void): Omit<Matcher<T>, 'whenNone'> {
cb();
return this;
}
}
function match<T extends Option>(input: T) {
return new Matcher(input);
}
function fa() {
return Math.random() > .5 ?
some(Math.random()) :
none();
}
const fav = fa();
if (fav.none) {
console.debug('none?', fav.some);
} else {
console.debug('some?', fav.some);
}
match(fav)
.whenNone(() => console.debug('nil'))
.whenSome((value) => console.debug({ value }));
const a = option(undefined);
type A = typeof a extends Some<infer R> ? R : None;
// ^?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment