Skip to content

Commit

Permalink
Add option class, fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac committed Nov 16, 2023
1 parent c70db1a commit 42def0c
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 356 deletions.
88 changes: 0 additions & 88 deletions src/none.ts

This file was deleted.

139 changes: 112 additions & 27 deletions src/option.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,114 @@
import type {inspectSymbol} from "./util"
import type {None} from "./none"
import type {Some} from "./some"

export interface OptionMethods<T> {
and<U>(other: Option<U>): Option<U>
andThen<U>(f: (value: T) => Option<U>): Option<U>
expect(panic: string): T
filter(f: (value: T) => boolean): Option<T>
inspect(f: (value: T) => void): Option<T>
map<U>(f: (value: T) => U): Option<U>
mapOr<A, B>(defaultValue: A, f: (value: T) => B): A | B
mapOrElse<A, B>(defaultValue: () => A, f: (value: T) => B): A | B
or<U>(other: Option<U>): Option<T | U>
orElse<U>(f: () => Option<U>): Option<T | U>
unwrap(): T
unwrapOr<U>(defaultValue: U): T | U
unwrapOrElse<U>(defaultValue: () => U): T | U
xor<U>(other: Option<U>): Option<T | U>

match<A, B>(some: (value: T) => A, none: () => B): A | B

toString(): `Some(${string})` | "None"
[inspectSymbol](): ReturnType<OptionMethods<T>["toString"]>
toObject(): {some: true; value: T} | {some: false; value: null}
toJSON(): {meta: "Some"; value: T} | {meta: "None"; value: null}
import {Panic} from "./panic"
import {inspectSymbol} from "./util"

export class OptionImpl<T> {
readonly some: boolean
readonly none: boolean
readonly value: T | never

constructor(value: T) {
this.some = true
this.none = false
this.value = value
}

and<U>(other: Option<U>): Option<T | U> {
return this.some ? other : None
}

andThen<U>(f: (value: T) => Option<U>): Option<T | U> {
return this.some ? f(this.value as T) : None
}

expect(panic: string): T {
if (this.some) {
return this.value as T
}
throw new Panic(panic)
}

filter(f: (value: T) => boolean): Option<T> {
return (this.some && f(this.value as T) ? this : None) as Option<T>
}

inspect(f: (value: T) => void): this {
if (this.some) {
f(this.value as T)
}
return this
}

map<U>(f: (value: T) => U): Option<T | U> {
return (this.some ? new OptionImpl(f(this.value as T)) : None) as Option<T | U>
}

mapOr<A, B>(defaultValue: A, f: (value: T) => B): A | B {
return this.some ? f(this.value as T) : defaultValue
}

mapOrElse<A, B>(defaultValue: () => A, f: (value: T) => B): A | B {
return this.some ? f(this.value as T) : defaultValue()
}

or<U>(other: Option<U>): Option<T | U> {
return (this.some ? this : other) as Option<T | U>
}

orElse<U>(f: () => Option<U>): Option<T | U> {
return (this.some ? this : f()) as Option<T | U>
}

unwrap(): T {
return this.value as T
}

unwrapOr<U>(defaultValue: U): T | U {
return this.some ? (this.value as T) : defaultValue
}

unwrapOrElse<U>(defaultValue: () => U): T | U {
return this.some ? (this.value as T) : defaultValue()
}

xor<U>(other: Option<U>): Option<T | U> {
return (other.some ? None : this) as Option<T | U>
}

match<A, B>(some: (value: T) => A, none: () => B): A | B {
return this.some ? some(this.value as T) : none()
}

toString(): `Some(${string})` | "None" {
return this.some ? `Some(${this.value})` : "None"
}

[inspectSymbol](): ReturnType<OptionImpl<T>["toString"]> {
return this.toString()
}

toObject(): {some: true; value: T} | {some: false; value: null} {
return this.some ? {some: true, value: this.value as T} : {some: false, value: null}
}

toJSON(): {meta: "Some"; value: T} | {meta: "None"; value: null} {
return this.some ? {meta: "Some", value: this.value as T} : {meta: "None", value: null}
}
}

export interface Some<T> extends OptionImpl<T> {
readonly some: true
readonly none: false
readonly value: T
}
export function Some<T>(value: T): Some<T> {
return new OptionImpl(value) as Some<T>
}

export interface None extends OptionImpl<never> {
readonly some: false
readonly none: true
readonly value: never
}
export const None = new OptionImpl(null) as None

export type Option<T> = (Some<T> | None) & OptionMethods<T>
export type Option<T> = (Some<T> | None) & OptionImpl<T>
4 changes: 2 additions & 2 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,6 @@ export class ResultImpl<T, E> {
}
}

export type Result<T, E> = Ok<T> | Err<E>

export interface Ok<T = null> extends ResultImpl<T, never> {
readonly ok: true
readonly err: false
Expand All @@ -161,3 +159,5 @@ export function Err<E>(value: E): Err<E>
export function Err<E>(value?: E): Err<E> {
return new ResultImpl<never, E>(false, (value ?? null) as E) as Err<E>
}

export type Result<T, E> = (Ok<T> | Err<E>) & ResultImpl<T, E>
95 changes: 0 additions & 95 deletions src/some.ts

This file was deleted.

Loading

0 comments on commit 42def0c

Please sign in to comment.