Skip to content

Instantly share code, notes, and snippets.

@bfncs
Last active August 17, 2018 10:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bfncs/4d4977483393315d4f3bf42d7cc43091 to your computer and use it in GitHub Desktop.
Save bfncs/4d4977483393315d4f3bf42d7cc43091 to your computer and use it in GitHub Desktop.
Slaying a UI Antipattern with TypeScript & TsMonad
import { Monad, Functor, Eq, eq } from 'tsmonad';
export enum RemoteDataType { NotAsked, Loading, Failure, Success }
export interface RemoteDataPatterns<E, D, T> {
notAsked: () => T;
loading: () => T;
failure: (l: E) => T;
success: (r: D) => T;
}
export default class RemoteData<E, D> implements Monad<D>, Functor<D>, Eq<RemoteData<E, D>> {
of = this.unit;
chain = this.bind;
lift = this.fmap;
map = this.fmap;
static notAsked<E, D>() {
return new RemoteData<E, D>(RemoteDataType.NotAsked);
}
static loading<E, D>() {
return new RemoteData<E, D>(RemoteDataType.Loading);
}
static failure<E, D>(error: E) {
return new RemoteData<E, D>(RemoteDataType.Failure, error);
}
static success<E, D>(data: D) {
return new RemoteData<E, D>(RemoteDataType.Success, undefined, data);
}
constructor(private type: RemoteDataType, private error?: E, private data?: D) {}
unit<T>(t: T) {
return RemoteData.success<E, T>(t);
}
bind<T>(f: (d: D) => RemoteData<E, T>) {
switch (this.type) {
case RemoteDataType.NotAsked:
return RemoteData.notAsked<E, T>();
case RemoteDataType.Loading:
return RemoteData.loading<E, T>();
case RemoteDataType.Failure:
return RemoteData.failure<E, T>(this.error as E);
case RemoteDataType.Success:
default:
return f(this.data as D);
}
}
fmap<T>(f: (d: D) => T) {
return this.bind(v => this.unit<T>(f(v)));
}
caseOf<T>(pattern: RemoteDataPatterns<E, D, T>) {
switch (this.type) {
case RemoteDataType.NotAsked:
return pattern.notAsked();
case RemoteDataType.Loading:
return pattern.loading();
case RemoteDataType.Failure:
return pattern.failure(this.error as E);
case RemoteDataType.Success:
default:
return pattern.success(this.data as D);
}
}
equals(other: RemoteData<E, D>) {
if (other.type !== this.type) {
return false;
}
switch (this.type) {
case RemoteDataType.NotAsked:
case RemoteDataType.Loading:
return true;
case RemoteDataType.Failure:
return eq(this.error, other.error);
case RemoteDataType.Success:
default:
return eq(this.data, other.data);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment