Properly document the Option type
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
All checks were successful
timw4mail/scroll/pipeline/head This commit looks good
This commit is contained in:
parent
d7bf8801c9
commit
b2169cf54b
@ -13,19 +13,19 @@ enum OptionType {
|
||||
|
||||
const isOption = <T>(v: any): v is Option<T> => v instanceof Option;
|
||||
|
||||
class OptionInnerNone {
|
||||
class OptNone {
|
||||
public type: OptionType = OptionType.None;
|
||||
}
|
||||
|
||||
class OptionInnerSome<T> {
|
||||
class OptSome<T> {
|
||||
public type: OptionType = OptionType.Some;
|
||||
|
||||
constructor(public value: T) {}
|
||||
}
|
||||
|
||||
type OptionInnerType<T> = OptionInnerNone | OptionInnerSome<T>;
|
||||
type OptType<T> = OptNone | OptSome<T>;
|
||||
|
||||
const isSome = <T>(v: OptionInnerType<T>): v is OptionInnerSome<T> =>
|
||||
const isSome = <T>(v: OptType<T>): v is OptSome<T> =>
|
||||
'value' in v && v.type === OptionType.Some;
|
||||
|
||||
/**
|
||||
@ -42,90 +42,211 @@ export class Option<T> {
|
||||
/**
|
||||
* Is this a 'Some' or a 'None'?
|
||||
*/
|
||||
private readonly inner: OptionInnerType<T>;
|
||||
private readonly inner: OptType<T>;
|
||||
|
||||
private constructor(v?: T) {
|
||||
this.inner = (v !== undefined && v !== null)
|
||||
? new OptionInnerSome(v)
|
||||
: new OptionInnerNone();
|
||||
? new OptSome(v)
|
||||
: new OptNone();
|
||||
}
|
||||
|
||||
/**
|
||||
* The equivalent of the Rust `Option`.`None` type
|
||||
* The equivalent of the Rust `Option`.`None` enum value
|
||||
*/
|
||||
public static get None(): Option<any> {
|
||||
return Option._None;
|
||||
}
|
||||
|
||||
public static Some<X>(v: any): Option<X> {
|
||||
return Option.from(v);
|
||||
/**
|
||||
* The equivalent of the Rust `Option`.`Some` enum value
|
||||
*
|
||||
* If the value passed is null or undefined, this will throw an Error
|
||||
*
|
||||
* @param v The value to wrap
|
||||
*/
|
||||
public static Some<X>(v: any): Option<X> | never {
|
||||
const maybeSome: Option<X> = Option.from(v);
|
||||
|
||||
if (maybeSome.isNone()) {
|
||||
throw new Error('Cannot create Some<T> with an empty value');
|
||||
} else {
|
||||
return maybeSome;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new `Option`
|
||||
*
|
||||
* If the value is null or undefined, the `Option` will have a `None` type
|
||||
*
|
||||
* @param v The value to wrap
|
||||
*/
|
||||
public static from<X>(v?: any): Option<X> {
|
||||
return (isOption(v)) ? Option.from(v.unwrap()) : new Option(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* The wrapped value is not null or undefined
|
||||
*/
|
||||
isSome(): boolean {
|
||||
return isSome(this.inner);
|
||||
}
|
||||
|
||||
/**
|
||||
* The wrapped value is null or undefined
|
||||
*/
|
||||
isNone(): boolean {
|
||||
return !this.isSome();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current `Option` is `Some`. If it is,
|
||||
* return the value of the passed function.
|
||||
*
|
||||
* Otherwise, returns false
|
||||
*
|
||||
* @param fn A boolean check to run on the wrapped value
|
||||
*/
|
||||
isSomeAnd(fn: (a: T) => boolean): boolean {
|
||||
return isSome(this.inner) ? fn(this.inner.value) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current `Option` is `None`. If it is,
|
||||
* return the value of the passed function.
|
||||
*
|
||||
* Otherwise, return false
|
||||
*
|
||||
* @param fn A function returning a boolean value
|
||||
*/
|
||||
isNoneAnd(fn: () => boolean): boolean {
|
||||
return this.isNone() ? fn() : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform the inner value of the `Option` with the passed function.
|
||||
* If this `Option` is `Some`, a new `Option` with the transformed value
|
||||
* is returned. Otherwise `None` is returned
|
||||
*
|
||||
* @param fn A function that takes the inner value of the `Option` and returns a new one
|
||||
*/
|
||||
map<U>(fn: (a: T) => U): Option<U> {
|
||||
return isSome(this.inner) ? Option.from(fn(this.inner.value)) : Option.None;
|
||||
}
|
||||
|
||||
mapOr<U>(def: U, f: (a: T) => U): U {
|
||||
return isSome(this.inner) ? f(this.inner.value) : def;
|
||||
/**
|
||||
* If this `Option` is `Some`, return the transformed inner value via the passed function.
|
||||
*
|
||||
* Otherwise, return the passed default value
|
||||
*
|
||||
* @param def The default value to return if this `Option` is `None`
|
||||
* @param fn A function that takes the inner value of this `Option` and returns a new value
|
||||
*/
|
||||
mapOr<U>(def: U, fn: (a: T) => U): U {
|
||||
return isSome(this.inner) ? fn(this.inner.value) : def;
|
||||
}
|
||||
|
||||
mapOrElse<U>(def: () => U, f: (a: T) => U): U {
|
||||
return isSome(this.inner) ? f(this.inner.value) : def();
|
||||
/**
|
||||
* If this `Option` is `Some`, return the transformed inner value via the passed function (fn).
|
||||
*
|
||||
* Otherwise run the function (def) to return a value
|
||||
*
|
||||
* @param def A function to return a value if this `Option` is `None`
|
||||
* @param fn A function that takes the inner value of this `Option` and returns a new value
|
||||
*/
|
||||
mapOrElse<U>(def: () => U, fn: (a: T) => U): U {
|
||||
return isSome(this.inner) ? fn(this.inner.value) : def();
|
||||
}
|
||||
|
||||
unwrap(): T | never {
|
||||
/**
|
||||
* Return the inner value if not `None`.
|
||||
* Otherwise, throw a new exception with the passed message
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
assert(err: string): T | never {
|
||||
if (isSome(this.inner)) {
|
||||
return this.inner.value;
|
||||
}
|
||||
|
||||
console.error('None.unwrap()');
|
||||
throw 'None.get';
|
||||
throw Error(err);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inner value if is `Some<T>`.
|
||||
*
|
||||
* If `None`, throws an exception.
|
||||
*/
|
||||
unwrap(): T | never {
|
||||
return this.assert("Called unwrap on a 'None'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inner value if is `Some<T>`,
|
||||
* Otherwise, return the passed default value
|
||||
*
|
||||
* @param def Value to return on `None` value
|
||||
*/
|
||||
unwrapOr(def: T): T {
|
||||
return isSome(this.inner) ? this.inner.value : def;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inner value if is `Some<T>`,
|
||||
* Otherwise, return the value generated by the passed function
|
||||
*
|
||||
* @param f Function to run on `None` value
|
||||
*/
|
||||
unwrapOrElse(f: () => T): T {
|
||||
return isSome(this.inner) ? this.inner.value : f();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this `Option` and the passed option are both `Some`,
|
||||
* otherwise return `None`
|
||||
*
|
||||
* @param optb Another `Option` to check
|
||||
*/
|
||||
and<U>(optb: Option<U>): Option<U> {
|
||||
return isSome(this.inner) ? optb : Option.None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this `Option` is `Some`. If it is, run the passed
|
||||
* function with the wrapped value.
|
||||
*
|
||||
* Otherwise, return None
|
||||
*
|
||||
* @param f function to run on the wrapped value
|
||||
*/
|
||||
andThen<U>(f: (a: T) => Option<U>): Option<U> {
|
||||
return isSome(this.inner) ? f(this.inner.value) : Option.None;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this `Option` is `None`. If it is, return the passed option.
|
||||
*
|
||||
* @param optb The `Option` to return if this `Option` is `None`
|
||||
*/
|
||||
or(optb: Option<T>): Option<T> {
|
||||
return this.isNone() ? optb : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this `Option` is `None`. If it is, return the passed function.
|
||||
*
|
||||
* Otherwise, return this `Option`
|
||||
*
|
||||
* @param f A function to return a different `Option`
|
||||
*/
|
||||
orElse(f: () => Option<T>): Option<T> {
|
||||
return this.isNone() ? f() : this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation of the `Option`,
|
||||
* mostly for debugging
|
||||
*/
|
||||
toString(): string {
|
||||
const innerValue = (isSome(this.inner))
|
||||
? JSON.stringify(this.inner.value)
|
||||
|
Loading…
Reference in New Issue
Block a user