diff --git a/typescript/option/src/helpers.ts b/typescript/option/src/helpers.ts new file mode 100644 index 0000000..9967f8a --- /dev/null +++ b/typescript/option/src/helpers.ts @@ -0,0 +1,110 @@ +import { Option, Some, None } from './types' +import { Binder, Folder, Mapper, Predicate, BackFolder } from './internalTypes' +import Internals, { SomeClass } from './internals' + +export const isSome = (option: Option) => + option instanceof Internals.SomeClass +export const isNothing = (option: Option) => + option instanceof Internals.NoneClass + +export const match = ( + option: Option, + caseSome: (v: T) => U, + caseNone: () => U +) => { + if (isSome(option)) { + return caseSome((option as SomeClass)[Internals.someValue]) + } + + return caseNone() +} + +export const bind = ( + binder: Binder, + option: Option +): Option => { + return match(option, binder, () => None) +} + +export const map = ( + mapper: Mapper, + option: Option +): Option => { + return match( + option, + v => Some(mapper(v)), + () => None + ) +} + +export const count = (option: Option) => Number(isSome(option)) + +export const exists = (predicate: Predicate, option: Option) => { + return match(option, predicate, () => false) +} + +export const filter = (predicate: Predicate, option: Option) => { + return match( + option, + v => (predicate(v) ? Some(v) : None), + () => None + ) +} + +export const fold = ( + folder: Folder, + initial: U, + option: Option +) => { + match( + option, + v => folder(initial, v), + () => initial + ) +} + +export const foldback = ( + folder: BackFolder, + option: Option, + initial: U +) => { + return match( + option, + v => folder(v, initial), + () => initial + ) +} + +export const forall = (predicate: Predicate, option: Option) => { + return match(option, predicate, () => true) +} + +export const get = (option: Option) => { + return match( + option, + v => v, + () => { + throw new Error('Cannot get value from None') + } + ) +} + +export const iter = (mapper: Mapper, option: Option) => { + match(option, mapper, () => {}) +} + +export const toArray = (option: Option) => { + return match( + option, + v => [v], + () => [] + ) +} + +export const toNullable = (option: Option) => { + return match( + option, + v => v, + () => null + ) +} diff --git a/typescript/option/src/index.ts b/typescript/option/src/index.ts index e69de29..493732d 100644 --- a/typescript/option/src/index.ts +++ b/typescript/option/src/index.ts @@ -0,0 +1,2 @@ +export * from './helpers' +export * from './types' diff --git a/typescript/option/src/internalTypes.ts b/typescript/option/src/internalTypes.ts new file mode 100644 index 0000000..a636b21 --- /dev/null +++ b/typescript/option/src/internalTypes.ts @@ -0,0 +1,7 @@ +import { Option } from './types' + +export type Mapper = (v: T) => U +export type Binder = Mapper> +export type Predicate = Mapper +export type Folder = (s: U, v: T) => U +export type BackFolder = (v: T, s: U) => U diff --git a/typescript/option/src/internals.ts b/typescript/option/src/internals.ts new file mode 100644 index 0000000..6e5911a --- /dev/null +++ b/typescript/option/src/internals.ts @@ -0,0 +1,17 @@ +export const isOption = Symbol('option') +export const someValue = Symbol('value') + +export class SomeClass { + public [isOption] = true + public [someValue]: T + + public constructor(value: T) { + this[someValue] = value + } +} + +export class NoneClass { + public [isOption] = true +} + +export default { NoneClass, SomeClass, isOption, someValue } diff --git a/typescript/option/src/types.ts b/typescript/option/src/types.ts new file mode 100644 index 0000000..3f55148 --- /dev/null +++ b/typescript/option/src/types.ts @@ -0,0 +1,6 @@ +import * as Internals from './internals' + +export type Option = Internals.SomeClass | Internals.NoneClass + +export const None = new Internals.NoneClass() +export const Some = (v: T) => new Internals.SomeClass(v)