export interface Ok<T> { isSuccessful: true; value: T; } export interface Err { isSuccessful: false; errorMessage: string; } export type Result<T> = Ok<T> | Err; export const andThen = <T, R>(fn: (a: T) => Result<R>) => (result: Result<T>) => { if (result.isSuccessful) { return fn(result.value); } else { return result; } } export function parseNotNull(data: any): Result<any> { if (data == null) return { isSuccessful: false, errorMessage: "Input is null." }; else return { isSuccessful: true, value: data }; } export function parseObject(data: any): Result<object> { if (typeof data === "object") return { isSuccessful: true, value: data }; else return { isSuccessful: false, errorMessage: "Input is not an object." }; } export const parseRequiredMember = <T>(fieldName: keyof T) => (data: any): Result<any> => { if (fieldName in data) { return { isSuccessful: true, value: data[fieldName] }; } else { return { isSuccessful: false, errorMessage: `Required member "${fieldName}" could not be found.` }; } } export const parseNullableMember = <T>(fieldName: keyof T) => (data: any): Result<any> => { if (fieldName in data) { return { isSuccessful: true, value: data[fieldName] }; } else { return { isSuccessful: true, value: null }; } } export function parseString(data: any): Result<string> { if (data == null || typeof data === "string") return { isSuccessful: true, value: data }; else return { isSuccessful: false, errorMessage: "Value is not a string." }; } export type ParseObject<T extends {}> = { [P in keyof T]: Result<T[P]> }; export function compose<R, S, T>(first: (obj:R) => Result<S>, second: (obj: S) => Result<T>): (obj:R) => Result<T> { return (obj:R): Result<T> => { const firstResult = first(obj); if(!firstResult.isSuccessful) return firstResult; return second(firstResult.value); }; } export function resolve<T extends {}>(members: ParseObject<T>): Result<T> { const obj: Partial<T> = {}; for (const key in members) { if (Object.prototype.hasOwnProperty.call(members, key)) { const member: Result<T[Extract<keyof T, string>]> = members[key]; if (!member.isSuccessful) { return member; } obj[key] = member.value; } } return { isSuccessful: true, value: (obj as T) }; }