Last active
January 19, 2024 17:00
-
-
Save tomiolah/d5e4cc37db7221a8be464a39d447f80e to your computer and use it in GitHub Desktop.
TS Utilities
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Represents a value that may or may not be present. */ | |
export type Maybe<T> = | |
| { verdict: "ok"; value: T } | |
| { verdict: "err"; error: string }; | |
/** Wraps a maybe value in a MaybeContainer. | |
* | |
* Why would you want to do this? Because it allows you to chain functions together using the pipe method. | |
* | |
* @param maybeValue - The maybe value to wrap. | |
* @returns A MaybeContainer containing the maybe value. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = wrap(maybeValue); | |
* const result = maybeContainer.handle((value) => value + 1); | |
* console.log(result); // 43 | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = wrap(maybeValue).pipe((value) => value + 1); | |
* console.log(result); // 43 | |
*/ | |
export class MaybeContainer<T> { | |
value: Maybe<T>; | |
/** Creates a new MaybeContainer instance by wrapping a maybe value. | |
* | |
* @param value - The maybe value to wrap. | |
*/ | |
constructor(value: Maybe<T>) { | |
this.value = value; | |
} | |
/** Handles the maybe value. | |
* | |
* @param onOk - The function to call if the maybe value is ok. | |
* @param onErr - The function to call if the maybe value is an error. | |
* @returns The result of the function that was called. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = new MaybeContainer(maybeValue); | |
* const result = maybeContainer.handle((value) => value + 1); | |
* console.log(result); // 43 | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "err", error: "Something went wrong" }; | |
* const maybeContainer = new MaybeContainer(maybeValue); | |
* const result = maybeContainer.handle((value) => value + 1, (err) => err); | |
* console.log(result); // "Something went wrong" | |
*/ | |
handle<RetVal = T>( | |
onOk: (value: T) => Maybe<RetVal>, | |
onErr: (err: string) => Maybe<RetVal> = (err) => { | |
console.error(err); | |
return { verdict: "err", error: err }; | |
} | |
): Maybe<RetVal> { | |
if (this.value.verdict === "ok") { | |
return onOk(this.value.value); | |
} | |
return onErr(this.value.error); | |
} | |
/** Unpacks the maybe value or panics (throws an error) if it is an error. | |
* | |
* @returns The value of the maybe value. If the maybe value is an error, this function will throw an error. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = new MaybeContainer(maybeValue); | |
* const result = maybeContainer.unpackOrPanic(); | |
* console.log(result); // 42 | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "err", error: "Something went wrong" }; | |
* const maybeContainer = new MaybeContainer(maybeValue); | |
* const result = maybeContainer.unpackOrPanic(); // Throws an error | |
*/ | |
unpackOrPanic(): T | undefined { | |
if (this.value.verdict === "ok") { | |
return this.value.value; | |
} | |
console.error(this.value.error); | |
throw new Error(this.value.error); | |
} | |
/** Wraps a maybe value in a MaybeContainer (same as the constructor). | |
* | |
* @param value - The maybe value to wrap. | |
* @returns A MaybeContainer containing the maybe value. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = MaybeContainer.wrap(maybeValue); | |
*/ | |
static wrap<X>(value: Maybe<X>): MaybeContainer<X> { | |
return new MaybeContainer(value); | |
} | |
/** Executes a function on the maybe value and returns the result in a new MaybeContainer. | |
* This allows you to chain functions together. | |
* | |
* @param fn - The function to execute on the maybe value. | |
* @returns A new MaybeContainer containing the result of the function. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = new MaybeContainer(maybeValue); | |
* const result = maybeContainer.pipe((value) => value + 1).pipe((value) => value + 1); | |
* console.log(result); // 44 | |
*/ | |
pipe<RetVal>(fn: (value: T) => Maybe<RetVal>): MaybeContainer<RetVal> { | |
return new MaybeContainer(this.handle(fn)); | |
} | |
} | |
export const M = { | |
/** Wraps a maybe value in a MaybeContainer. | |
* Same as MaybeContainer.wrap() or new MaybeContainer(). | |
* | |
* @param maybeValue - The maybe value to wrap. | |
* @returns A MaybeContainer containing the maybe value. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const maybeContainer = wrap(maybeValue); | |
* const result = maybeContainer.handle((value) => value + 1); | |
* console.log(result); // 43 | |
*/ | |
wrap: <T>(maybeValue: Maybe<T>): MaybeContainer<T> => | |
MaybeContainer.wrap(maybeValue), | |
/** Creates a maybe value that is ok. | |
* | |
* @param value - The value to wrap in a maybe value. | |
* @returns A maybe value that is ok. | |
* @example | |
* const maybeValue = Ok(42); | |
* console.log(maybeValue); // { verdict: "ok", value: 42 } | |
*/ | |
Ok: <T>(value: T): Maybe<T> => ({ verdict: "ok", value }), | |
/** Creates a maybe value that is an error. | |
* | |
* @param error - The error message to wrap in a maybe value. | |
* @returns A maybe value that is an error. | |
* @example | |
* const maybeValue = Err("Something went wrong"); | |
* console.log(maybeValue); // { verdict: "err", error: "Something went wrong" } | |
*/ | |
Err: <T>(error: string): Maybe<T> => ({ verdict: "err", error }), | |
/** Unpacks a maybe value or panics (throws an error) if it is an error. | |
* | |
* @param maybeValue - The maybe value to unpack. | |
* @returns The value of the maybe value. If the maybe value is an error, this function will throw an error. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const result = unpackOrPanic(maybeValue); | |
* console.log(result); // 42 | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "err", error: "Something went wrong" }; | |
* const result = unpackOrPanic(maybeValue); // Throws an error | |
*/ | |
unpackOrPanic: <T>(maybeValue: Maybe<T>): T | undefined => { | |
if (maybeValue.verdict === "ok") { | |
return maybeValue.value; | |
} | |
console.error(maybeValue.error); | |
throw new Error(maybeValue.error); | |
}, | |
/** | |
* Handles a maybe value. | |
* | |
* @param maybeValue - The maybe value to handle. | |
* @param onOk - The function to call if the maybe value is ok. | |
* @param onErr - The function to call if the maybe value is an error. | |
* @returns The result of the function that was called. | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "ok", value: 42 }; | |
* const result = handleMaybe(maybeValue, (value) => value + 1); | |
* console.log(result); // 43 | |
* @example | |
* const maybeValue: Maybe<number> = { verdict: "err", error: "Something went wrong" }; | |
* const result = handleMaybe(maybeValue, (value) => value + 1, (err) => err); | |
* console.log(result); // "Something went wrong" | |
*/ | |
handleMaybe: <T, RetVal = T>( | |
maybeValue: Maybe<T>, | |
onOk: (value: T) => Maybe<RetVal>, | |
onErr: (err: string) => Maybe<RetVal> = (err) => { | |
console.error(err); | |
return { verdict: "err", error: err }; | |
} | |
): Maybe<RetVal> => { | |
if (maybeValue.verdict === "ok") { | |
return onOk(maybeValue.value); | |
} | |
return onErr(maybeValue.error); | |
}, | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Creates a sorting function based on the provided compare function. | |
* @param compareFn - The compare function used to determine the order of elements. | |
* @returns A sorting function that can be used with Array.sort(). | |
*/ | |
export function sortFn<T>(compareFn: (a: T, b: T) => "a>b" | "a<b" | "a=b") { | |
return (a: T, b: T) => { | |
const result = compareFn(a, b); | |
if (result === "a>b") return 1; | |
if (result === "a<b") return -1; | |
return 0; | |
}; | |
} | |
/** Compare function helper | |
* | |
* Using the `Array.sort` method is usually rather confusing, so this helper | |
* function makes it easier to understand what's going on. | |
* It translates easy-to-understand comparison results like "a<b", "a>b" or "a=b" | |
* to the -1, 1 or 0 values that the `Array.sort` method expects. | |
* | |
* @param cmp - The comparison function that returns "a<b", "a>b" or "a=b" depending on the comparison result. | |
* This result is then converted to -1, 1 or 0, respectively, to be used by the Array.sort method | |
* @returns a function that can be used as a parameter for the Array.sort method to compare two values | |
*/ | |
export const compareFn = | |
<T>(cmp: (a: T, b: T) => "a<b" | "a>b" | "a=b") => | |
(a: T, b: T) => { | |
const result = cmp(a, b); | |
if (result === "a<b") { | |
return -1; | |
} | |
if (result === "a>b") { | |
return 1; | |
} | |
return 0; | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment