UNPKG

resultable

Version:

A small package to handle errors as values

1 lines 13.2 kB
{"version":3,"sources":["../src/result.ts","../src/match.ts"],"sourcesContent":["import { MakeMatchObj, matchBrand } from \"./match\";\r\nimport { Equals } from \"./util\";\r\n\r\nexport const TypeId: unique symbol = Symbol.for(\"@Shared/BaseError\");\r\nexport type TypeId = typeof TypeId;\r\n\r\nexport type BaseError<T extends string> = Error & {\r\n readonly [TypeId]: TypeId;\r\n readonly __brand: T;\r\n};\r\n\r\nexport function BrandedError<T extends string>(\r\n brand: T\r\n): new <Args extends Record<string, any> = {}>(\r\n args: Equals<Args, {}> extends true\r\n ? void\r\n : { readonly [P in keyof Args as P extends \"__brand\" ? never : P]: Args[P] }\r\n) => BaseError<T> & Args {\r\n class BaseBrandedError extends Error {\r\n readonly [TypeId]: TypeId = TypeId;\r\n readonly __brand: T = brand;\r\n\r\n constructor(args?: Record<string, any>) {\r\n super(args?.message || \"An error occurred\");\r\n\r\n if (args) {\r\n Object.assign(this, args);\r\n }\r\n }\r\n }\r\n\r\n (BaseBrandedError.prototype as any).name = brand;\r\n\r\n return BaseBrandedError as any;\r\n}\r\n\r\nexport class UnknownException extends BrandedError(\"@Shared/UnknownException\")<{\r\n message?: string;\r\n cause?: unknown;\r\n}> {\r\n constructor(\r\n args: { message?: string; cause?: unknown } = { message: \"Unknown exception\" }\r\n ) {\r\n super(args);\r\n }\r\n}\r\n\r\nexport type OkResult<T> = Readonly<[value: T, error: undefined]>;\r\nexport type ErrorResult<E extends BaseError<string>> = Readonly<\r\n [value: undefined, error: E]\r\n>;\r\nexport type Result<T, E extends BaseError<string>> =\r\n | OkResult<T>\r\n | ErrorResult<E>;\r\n\r\ntype ExtractOk<T> = T extends OkResult<infer U> ? U : never;\r\ntype ExtractErr<T> = T extends ErrorResult<infer E> ? E : never;\r\n\r\nexport type MergeResults<TUnion extends OkResult<any> | ErrorResult<any>> =\r\n ExtractErr<TUnion> extends never\r\n ? OkResult<ExtractOk<TUnion>>\r\n : ExtractOk<TUnion> extends never\r\n ? ErrorResult<ExtractErr<TUnion>>\r\n : Result<ExtractOk<TUnion>, ExtractErr<TUnion>>; // Ambos casos\r\n\r\ntype NormalizeResult<R> = R extends OkResult<infer T>\r\n ? R\r\n : R extends ErrorResult<infer E>\r\n ? R\r\n : R extends BaseError<infer E>\r\n ? ErrorResult<R>\r\n : never;\r\n\r\nexport const resultableFn = <\r\n Params extends any[],\r\n TUnion extends\r\n | OkResult<any>\r\n | ErrorResult<BaseError<string>>\r\n | BaseError<string>\r\n>(\r\n fn: (...args: Params) => Promise<TUnion>\r\n): ((...args: Params) => Promise<MergeResults<NormalizeResult<TUnion>>>) => {\r\n const callback = async (...args: Params) => {\r\n const result = await fn(...args);\r\n\r\n if (isBrandedError(result)) {\r\n return err(result);\r\n }\r\n\r\n return result;\r\n };\r\n\r\n return callback as any;\r\n};\r\n\r\nexport function ok<T>(value: T): OkResult<T> {\r\n return [value, undefined];\r\n}\r\n\r\nexport function err<E extends BaseError<any>>(error: E): ErrorResult<E> {\r\n return [undefined, error];\r\n}\r\n\r\nexport function okVoid(): OkResult<void> {\r\n return [undefined, undefined];\r\n}\r\n\r\nexport function fail(\r\n args: ConstructorParameters<typeof UnknownException>[0] = {}\r\n): ErrorResult<UnknownException> {\r\n return err(new UnknownException(args));\r\n}\r\n\r\nexport function tryCatch<T>(\r\n fn: () => Promise<T>\r\n): Promise<Result<T, UnknownException>>;\r\n\r\nexport function tryCatch<T, E extends BaseError<string>>(\r\n fn: () => Promise<T>,\r\n errorFn: (cause: unknown) => E\r\n): Promise<Result<T, E>>;\r\n\r\nexport async function tryCatch<T, E extends BaseError<string>>(\r\n fn: () => Promise<T>,\r\n errorFn?: (cause: unknown) => E\r\n): Promise<Result<T, E>> {\r\n try {\r\n return ok(await fn());\r\n } catch (e) {\r\n return err(errorFn?.(e) ?? (new UnknownException({ cause: e }) as any));\r\n }\r\n}\r\n\r\ntype Mapper<T, R> = (value: T) => R;\r\n\r\nexport function map<T, E extends BaseError<string>, R>(\r\n mapper: Mapper<T, R>\r\n): (result: Result<T, E>) => Result<R, E>;\r\n\r\nexport function map<T, E extends BaseError<string>, R>(\r\n result: Result<T, E>,\r\n mapper: Mapper<T, R>\r\n): Result<R, E>;\r\n\r\nexport function map<T, E extends BaseError<string>, R>(\r\n resultOrMapper: Result<T, E> | Mapper<T, R>,\r\n mapper?: Mapper<T, R>\r\n) {\r\n const finalMapper =\r\n typeof resultOrMapper === \"function\" ? resultOrMapper : mapper!;\r\n\r\n const fn = (result: Result<T, E>) => {\r\n const [value, error] = result;\r\n\r\n if (error) {\r\n return err(error);\r\n }\r\n\r\n return ok(finalMapper(value as T));\r\n };\r\n\r\n return typeof resultOrMapper === \"function\" ? fn : fn(resultOrMapper);\r\n}\r\n\r\nexport function mapErr<\r\n T,\r\n E extends BaseError<string>,\r\n R extends BaseError<string>\r\n>(mapper: Mapper<E, R>): (result: Result<T, E>) => Result<T, R>;\r\n\r\nexport function mapErr<\r\n T,\r\n E extends BaseError<string>,\r\n R extends BaseError<string>\r\n>(result: Result<T, E>, mapper: Mapper<E, R>): Result<T, R>;\r\n\r\nexport function mapErr<\r\n T,\r\n E extends BaseError<string>,\r\n R extends BaseError<string>\r\n>(resultOrMapper: Result<T, E> | Mapper<E, R>, mapper?: Mapper<E, R>) {\r\n const finalMapper =\r\n typeof resultOrMapper === \"function\" ? resultOrMapper : mapper!;\r\n\r\n const fn = (result: Result<T, E>) => {\r\n const [value, error] = result;\r\n\r\n if (error) {\r\n return err(finalMapper(error as E));\r\n }\r\n\r\n return ok(value as T);\r\n };\r\n\r\n return typeof resultOrMapper === \"function\" ? fn : fn(resultOrMapper);\r\n}\r\n\r\nexport function catchAllErr<T, E extends BaseError<string>, R>(\r\n mapper: Mapper<E, R>\r\n): (result: Result<T, E>) => OkResult<R>;\r\n\r\nexport function catchAllErr<T, E extends BaseError<string>, R>(\r\n result: Result<T, E>,\r\n mapper: Mapper<E, R>\r\n): OkResult<R>;\r\n\r\nexport function catchAllErr<T, E extends BaseError<string>, R>(\r\n resultOrMapper: Result<T, E> | Mapper<E, R>,\r\n mapper?: Mapper<E, R>\r\n) {\r\n const finalMapper =\r\n typeof resultOrMapper === \"function\" ? resultOrMapper : mapper!;\r\n\r\n const fn = (result: Result<T, E>) => {\r\n const [value, error] = result;\r\n\r\n if (error) {\r\n return ok(finalMapper(error as E));\r\n }\r\n\r\n return ok(value as T);\r\n };\r\n\r\n return typeof resultOrMapper === \"function\" ? fn : fn(resultOrMapper);\r\n}\r\n\r\ntype InferBrand<E extends BaseError<string>> = E extends BaseError<infer B>\r\n ? B\r\n : never;\r\n\r\nexport function catchAllBrands<\r\n T,\r\n E extends BaseError<string>,\r\n R,\r\n MatchObject extends MakeMatchObj<\"__brand\", E, R>,\r\n R2 extends ReturnType<MatchObject[InferBrand<E>]>\r\n>(brandMatchers: MatchObject): (result: Result<T, E>) => OkResult<T | R2>;\r\n\r\nexport function catchAllBrands<\r\n T,\r\n E extends BaseError<string>,\r\n R,\r\n MatchObject extends MakeMatchObj<\"__brand\", E, R>,\r\n R2 extends ReturnType<MatchObject[InferBrand<E>]>\r\n>(result: Result<T, E>, brandMatchers: MatchObject): OkResult<T | R2>;\r\n\r\nexport function catchAllBrands<\r\n T,\r\n E extends BaseError<string>,\r\n R,\r\n MatchObject extends MakeMatchObj<\"__brand\", E, R>,\r\n R2 extends ReturnType<MatchObject[InferBrand<E>]>\r\n>(\r\n resultOrBrandMappers: Result<T, E> | MatchObject,\r\n brandMatchers?: MatchObject\r\n): OkResult<T | R2> | ((result: Result<T, E>) => OkResult<T | R2>) {\r\n const isResult = (value: unknown): value is Result<T, E> =>\r\n Array.isArray(value);\r\n\r\n const finalMatchers = isResult(resultOrBrandMappers)\r\n ? brandMatchers!\r\n : resultOrBrandMappers;\r\n\r\n const fn = (result: Result<T, E>) => {\r\n const [value, error] = result;\r\n\r\n if (error) {\r\n return ok(matchBrand(error)(finalMatchers));\r\n }\r\n\r\n return ok(value as T);\r\n };\r\n\r\n return (\r\n isResult(resultOrBrandMappers) ? fn(resultOrBrandMappers) : fn\r\n ) as any;\r\n}\r\n\r\nexport function unwrap<T>(result: OkResult<T>): T {\r\n return result[0];\r\n}\r\n\r\nexport function unwrapErr<E extends BaseError<string>>(\r\n result: ErrorResult<E>\r\n): E {\r\n return result[1];\r\n}\r\n\r\nexport function isErr<T, E extends BaseError<string>>(\r\n result: Result<T, E>\r\n): result is ErrorResult<E> {\r\n const [, error] = result;\r\n\r\n return typeof error !== \"undefined\" && error[TypeId] === TypeId;\r\n}\r\n\r\nexport function isBrandedError(value: unknown): value is BaseError<any> {\r\n return (\r\n typeof value === \"object\" &&\r\n value !== null &&\r\n TypeId in value &&\r\n value[TypeId] === TypeId\r\n );\r\n}\r\n\r\nexport function exhaustiveSwitchGuard(_: never): never {\r\n throw new Error(\"Exhaustive switch guard, should not have reached here.\");\r\n}\r\n","/**\r\n * Describes the match object, where the keys are the discriminant ids, and the values\r\n * are the functions which handle the value\r\n */\r\nexport type MakeMatchObj<D extends string, ADT extends Record<D, string>, Z> = {\r\n [K in ADT[D]]: (v: MakeADTMember<D, ADT, K>) => Z;\r\n};\r\n\r\n/**\r\n * Unions all the return types of matcher functions\r\n */\r\nexport type MakeReturns<\r\n D extends string,\r\n ADT extends Record<D, string>,\r\n M extends MakeMatchObj<D, ADT, unknown>,\r\n> = {\r\n [K in keyof M]: ReturnType<M[K]>;\r\n}[keyof M];\r\n\r\n/**\r\n * Helper type for extracting a member from an ADT\r\n */\r\nexport type MakeADTMember<D extends string, ADT, Type extends string> = Extract<\r\n ADT,\r\n Record<D, Type>\r\n>;\r\n\r\n// export function makeMatchI<D extends string>(\r\n// d: D,\r\n// ): <ADT extends Record<D, string>>(\r\n// v: ADT,\r\n// ) => <M extends MakeMatchObj<D, ADT, unknown>>(matchObj: M) => MakeReturns<D, ADT, M> {\r\n// return (v) => (matchObj) => matchObj[v[d]](v as any) as any;\r\n// }\r\nexport function makeMatchI<D extends string>(d: D) {\r\n return <ADT extends Record<D, string>>(v: ADT) => {\r\n return <M extends MakeMatchObj<D, ADT, unknown>>(matchObj: M): MakeReturns<D, ADT, M> => {\r\n return matchObj[v[d]](v as any) as any;\r\n };\r\n };\r\n}\r\nexport const matchBrand = makeMatchI(\"__brand\");\r\n"],"mappings":"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","names":[]}