According to Wikipedia, a monad is a design pattern that combines functions and wraps their return types in a wrapped type with additional computation. In practical terms: take a type T, and create a pipeline of chained functions that perform computation on an argument of type T, where each function takes into account the state returned by the previous call.
Three components make up a monad:
- A wrapper type
- A wrap function
- A run function

The requirements that drove this implementation:
- Access nested properties without null-checking at each step
- Each chained step can return a different
TinOption<T>since we're traversing properties
The implementation:
- A simple object containing the wrapped value, the wrap function, and the run function
Runreturns the sameT;RunTransformreturns a newTNewValueOrNullandValueOrare convenience methods beyond the formal monad pattern
typescript
export class Option<T> {
private readonly _value: T | null | undefined;
private readonly _hasValue: boolean;
public get Value(): T {
if (!this._hasValue) {
throw `This option does not have a value`;
}
return this._value as T;
}
public get HasValue(): boolean {
return this._hasValue;
}
constructor(value: T | null | undefined) {
if (value === null || value === undefined) {
this._hasValue = false;
this._value = null;
} else {
this._value = value;
this._hasValue = true;
}
}
public static From<T>(value: T | null | undefined): Option<T> {
return new Option<T>(value);
}
public Run(execute: (value: T) => Option<T>): Option<T> {
return OptionRun(this, execute);
}
public RunTransform<TNew>(execute: (value: T) => Option<TNew> | TNew): Option<TNew> {
return OptionRunTransform<T, TNew>(this, tr => {
const res = execute(tr);
if (res instanceof Option) {
return res as Option<TNew>;
} else {
return Option.From(res as TNew);
}
});
}
public ValueOr(defaultValue: T): T {
return this._hasValue ? this.Value : defaultValue;
}
public ValueOrNull(): T | null {
return this._hasValue ? this.Value : null;
}
}
export function OptionRun<T>(input: Option<T>, functor: (value: T) => Option<T>): Option<T> {
if (input.HasValue) {
return functor(input.Value);
}
return input;
}
export function OptionRunTransform<T, TNew>(
input: Option<T>,
functor: (value: T) => Option<TNew>
): Option<TNew> {
if (input.HasValue) {
return functor(input.Value);
}
return Option.From<TNew>(null);
}Usage
With the Option monad:
typescript
const name = Option.From(activity)
.RunTransform(r => r.ActivityTypeNode)
.RunTransform(r => r.ActivityType)
.RunTransform(r => r.Name)
.ValueOr('');Without:
typescript
const name = (activity
&& activity.ActivityTypeNode
&& activity.ActivityTypeNode.ActivityType
&& activity.ActivityTypeNode.ActivityType.Name)
? activity.ActivityTypeNode.ActivityType.Name
: '';