@j2blasco/ts-result 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +49 -48
- package/src/index.ts +2 -0
- package/src/result-functions.test.ts +113 -0
- package/src/result-functions.ts +41 -0
- package/src/result.test.ts +113 -0
- package/src/result.ts +158 -0
- package/src/utils/test/assert-type.ts +7 -0
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 [Your Name]
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 [Your Name]
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/result.ts","../src/result-functions.ts"],"sourcesContent":["export * from './result';\
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/result.ts","../src/result-functions.ts"],"sourcesContent":["export * from './result';\nexport * from './result-functions';","export interface Result<TSuccess1, TError1> {\n andThen<TResult extends Result<any, any>>(\n f: (value: TSuccess1) => TResult,\n ): Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>;\n andThenAsync<TResult extends Result<any, any>>(\n f: (value: TSuccess1) => Promise<TResult>,\n ): Promise<Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>>;\n catchError<TResult extends Result<any, any>>(\n f: (error: TError1) => TResult,\n ): Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>;\n catchErrorAsync<TResult extends Result<any, any>>(\n f: (error: TError1) => Promise<TResult>,\n ): Promise<Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>>;\n unwrapOrThrow(errorCallback?: (e: TError1) => void): TSuccess1;\n}\n\nexport function unwrapSuccessResult<T>(result: Result<T, never>): T {\n if (result instanceof ResultSuccessImp) {\n return result['value'];\n }\n throw new Error('Unreachable code. Result is an error');\n}\n\nexport type ErrorWithCode<TCode, TData = object> = { code: TCode; data: TData };\nexport function errorWithCode<TCode, TData>(\n code: TCode,\n data: TData,\n): ErrorWithCode<TCode, TData> {\n return {\n code,\n data,\n };\n}\n\nexport type ErrorUnknown = {\n code: 'unknown';\n data: { message: string };\n};\nexport function errorUnkown(message: string): ErrorUnknown {\n return {\n code: 'unknown',\n data: { message },\n };\n}\n\nexport type SuccessType<T> = T extends Result<infer S, any> ? S : never;\nexport type ErrorType<T> = T extends Result<any, infer E> ? E : never;\n\nexport type SuccessVoid = never;\n\nexport function resultSuccessVoid(): Result<SuccessVoid, never> {\n return new ResultSuccessImp<SuccessVoid>(undefined as never);\n}\n\nexport function resultSuccess<TSuccess1>(\n value: TSuccess1,\n): Result<TSuccess1, never> {\n return new ResultSuccessImp<TSuccess1>(value);\n}\n\ntype EnforceLiteral<T> = string extends T ? never : T;\n\nexport class resultError {\n public static unknown(message: string): Result<never, ErrorUnknown> {\n return new ResultErrorImp<ErrorUnknown>(errorUnkown(message));\n }\n public static withCode<TCode, TData = object>(\n code: EnforceLiteral<TCode>,\n data: TData = {} as TData,\n ): Result<never, ErrorWithCode<TCode, TData>> {\n return new ResultErrorImp<ErrorWithCode<TCode, TData>>(\n errorWithCode(code, data),\n );\n }\n public static fromError<\n TError1 extends ErrorWithCode<any, object>,\n >(error: TError1): Result<never, TError1> {\n return new ResultErrorImp<TError1>(error);\n }\n}\n\nclass ResultSuccessImp<TSuccess1> implements Result<TSuccess1, never> {\n constructor(private value: TSuccess1) {}\n\n public andThen<TSuccess2, TError2>(\n f: (value: TSuccess1) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError2> {\n return f(this.value);\n }\n\n public async andThenAsync<TSuccess2, TError2>(\n f: (value: TSuccess1) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError2>> {\n return await f(this.value);\n }\n\n public catchError<TSuccess2, TError2>(\n _f: (error: never) => Result<TSuccess2, TError2>,\n ): Result<TSuccess1 | TSuccess2, TError2> {\n return this;\n }\n\n public async catchErrorAsync<TSuccess2, TError2>(\n _f: (error: never) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess1 | TSuccess2, TError2>> {\n return this;\n }\n\n public unwrapOrThrow(_errorCallback?: (e: never) => void): TSuccess1 {\n return this.value;\n }\n}\n\nclass ResultErrorImp<TError1> implements Result<never, TError1> {\n constructor(private error: TError1) {}\n\n public andThen<TSuccess2, TError2>(\n _f: (value: never) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError1 | TError2> {\n return this;\n }\n\n public async andThenAsync<TSuccess2, TError2>(\n _f: (value: never) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError1 | TError2>> {\n return this;\n }\n\n public catchError<TSuccess2, TError2>(\n f: (error: TError1) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError2> {\n return f(this.error);\n }\n\n public async catchErrorAsync<TSuccess2, TError2>(\n f: (error: TError1) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError2>> {\n return await f(this.error);\n }\n\n public unwrapOrThrow(errorCallback?: (e: TError1) => void): never {\n if (errorCallback !== undefined) {\n errorCallback(this.error);\n }\n throw this.error;\n }\n}\n\nexport type UnwrapErrorFromResult<T> =\n T extends Result<any, infer U> ? U : never;\n\nexport function makeSafePromise<T>(\n promise: Promise<T>,\n): Promise<Result<T, ErrorUnknown>> {\n return promise\n .then((value) => resultSuccess(value))\n .catch((error) => resultError.unknown(error.message));\n}","import { ErrorType, Result, SuccessType } from \"./result\";\n\nexport function andThen<TSuccess, TError, TResult extends Result<any, any>>(\n callback: (value: TSuccess) => TResult\n): (\n result: Result<TSuccess, TError>\n) => Result<SuccessType<TResult>, TError | ErrorType<TResult>> {\n return (result) => result.andThen(callback);\n}\n\nexport function andThenAsync<\n TSuccess,\n TError,\n TResult extends Result<any, any>\n>(\n callback: (value: TSuccess) => Promise<TResult>\n): (\n result: Result<TSuccess, TError>\n) => Promise<Result<SuccessType<TResult>, TError | ErrorType<TResult>>> {\n return (result) => result.andThenAsync(callback);\n}\n\nexport function catchError<TSuccess, TError, TResult extends Result<any, any>>(\n callback: (error: TError) => TResult\n): (\n result: Result<TSuccess, TError>\n) => Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>> {\n return (result) => result.catchError(callback);\n}\n\nexport function catchErrorAsync<\n TSuccess,\n TError,\n TResult extends Result<any, any>\n>(\n callback: (error: TError) => Promise<TResult>\n): (\n result: Result<TSuccess, TError>\n) => Promise<Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>>> {\n return (result) => result.catchErrorAsync(callback);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACgBO,SAAS,oBAAuB,QAA6B;AAClE,MAAI,kBAAkB,kBAAkB;AACtC,WAAO,OAAO,OAAO;AAAA,EACvB;AACA,QAAM,IAAI,MAAM,sCAAsC;AACxD;AAGO,SAAS,cACd,MACA,MAC6B;AAC7B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,YAAY,SAA+B;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,EAAE,QAAQ;AAAA,EAClB;AACF;AAOO,SAAS,oBAAgD;AAC9D,SAAO,IAAI,iBAA8B,MAAkB;AAC7D;AAEO,SAAS,cACd,OAC0B;AAC1B,SAAO,IAAI,iBAA4B,KAAK;AAC9C;AAIO,IAAM,cAAN,MAAkB;AAAA,EACvB,OAAc,QAAQ,SAA8C;AAClE,WAAO,IAAI,eAA6B,YAAY,OAAO,CAAC;AAAA,EAC9D;AAAA,EACA,OAAc,SACZ,MACA,OAAc,CAAC,GAC6B;AAC5C,WAAO,IAAI;AAAA,MACT,cAAc,MAAM,IAAI;AAAA,IAC1B;AAAA,EACF;AAAA,EACA,OAAc,UAEZ,OAAwC;AACxC,WAAO,IAAI,eAAwB,KAAK;AAAA,EAC1C;AACF;AAEA,IAAM,mBAAN,MAAsE;AAAA,EACpE,YAAoB,OAAkB;AAAlB;AAAA,EAAmB;AAAA,EAEhC,QACL,GAC4B;AAC5B,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAAA,EAEA,MAAa,aACX,GACqC;AACrC,WAAO,MAAM,EAAE,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEO,WACL,IACwC;AACxC,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,gBACX,IACiD;AACjD,WAAO;AAAA,EACT;AAAA,EAEO,cAAc,gBAAgD;AACnE,WAAO,KAAK;AAAA,EACd;AACF;AAEA,IAAM,iBAAN,MAAgE;AAAA,EAC9D,YAAoB,OAAgB;AAAhB;AAAA,EAAiB;AAAA,EAE9B,QACL,IACsC;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,aACX,IAC+C;AAC/C,WAAO;AAAA,EACT;AAAA,EAEO,WACL,GAC4B;AAC5B,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAAA,EAEA,MAAa,gBACX,GACqC;AACrC,WAAO,MAAM,EAAE,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEO,cAAc,eAA6C;AAChE,QAAI,kBAAkB,QAAW;AAC/B,oBAAc,KAAK,KAAK;AAAA,IAC1B;AACA,UAAM,KAAK;AAAA,EACb;AACF;AAKO,SAAS,gBACd,SACkC;AAClC,SAAO,QACJ,KAAK,CAAC,UAAU,cAAc,KAAK,CAAC,EACpC,MAAM,CAAC,UAAU,YAAY,QAAQ,MAAM,OAAO,CAAC;AACxD;;;AC3JO,SAAS,QACd,UAG6D;AAC7D,SAAO,CAAC,WAAW,OAAO,QAAQ,QAAQ;AAC5C;AAEO,SAAS,aAKd,UAGsE;AACtE,SAAO,CAAC,WAAW,OAAO,aAAa,QAAQ;AACjD;AAEO,SAAS,WACd,UAG+D;AAC/D,SAAO,CAAC,WAAW,OAAO,WAAW,QAAQ;AAC/C;AAEO,SAAS,gBAKd,UAGwE;AACxE,SAAO,CAAC,WAAW,OAAO,gBAAgB,QAAQ;AACpD;","names":[]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/result.ts","../src/result-functions.ts"],"sourcesContent":["export interface Result<TSuccess1, TError1> {\
|
|
1
|
+
{"version":3,"sources":["../src/result.ts","../src/result-functions.ts"],"sourcesContent":["export interface Result<TSuccess1, TError1> {\n andThen<TResult extends Result<any, any>>(\n f: (value: TSuccess1) => TResult,\n ): Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>;\n andThenAsync<TResult extends Result<any, any>>(\n f: (value: TSuccess1) => Promise<TResult>,\n ): Promise<Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>>;\n catchError<TResult extends Result<any, any>>(\n f: (error: TError1) => TResult,\n ): Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>;\n catchErrorAsync<TResult extends Result<any, any>>(\n f: (error: TError1) => Promise<TResult>,\n ): Promise<Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>>;\n unwrapOrThrow(errorCallback?: (e: TError1) => void): TSuccess1;\n}\n\nexport function unwrapSuccessResult<T>(result: Result<T, never>): T {\n if (result instanceof ResultSuccessImp) {\n return result['value'];\n }\n throw new Error('Unreachable code. Result is an error');\n}\n\nexport type ErrorWithCode<TCode, TData = object> = { code: TCode; data: TData };\nexport function errorWithCode<TCode, TData>(\n code: TCode,\n data: TData,\n): ErrorWithCode<TCode, TData> {\n return {\n code,\n data,\n };\n}\n\nexport type ErrorUnknown = {\n code: 'unknown';\n data: { message: string };\n};\nexport function errorUnkown(message: string): ErrorUnknown {\n return {\n code: 'unknown',\n data: { message },\n };\n}\n\nexport type SuccessType<T> = T extends Result<infer S, any> ? S : never;\nexport type ErrorType<T> = T extends Result<any, infer E> ? E : never;\n\nexport type SuccessVoid = never;\n\nexport function resultSuccessVoid(): Result<SuccessVoid, never> {\n return new ResultSuccessImp<SuccessVoid>(undefined as never);\n}\n\nexport function resultSuccess<TSuccess1>(\n value: TSuccess1,\n): Result<TSuccess1, never> {\n return new ResultSuccessImp<TSuccess1>(value);\n}\n\ntype EnforceLiteral<T> = string extends T ? never : T;\n\nexport class resultError {\n public static unknown(message: string): Result<never, ErrorUnknown> {\n return new ResultErrorImp<ErrorUnknown>(errorUnkown(message));\n }\n public static withCode<TCode, TData = object>(\n code: EnforceLiteral<TCode>,\n data: TData = {} as TData,\n ): Result<never, ErrorWithCode<TCode, TData>> {\n return new ResultErrorImp<ErrorWithCode<TCode, TData>>(\n errorWithCode(code, data),\n );\n }\n public static fromError<\n TError1 extends ErrorWithCode<any, object>,\n >(error: TError1): Result<never, TError1> {\n return new ResultErrorImp<TError1>(error);\n }\n}\n\nclass ResultSuccessImp<TSuccess1> implements Result<TSuccess1, never> {\n constructor(private value: TSuccess1) {}\n\n public andThen<TSuccess2, TError2>(\n f: (value: TSuccess1) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError2> {\n return f(this.value);\n }\n\n public async andThenAsync<TSuccess2, TError2>(\n f: (value: TSuccess1) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError2>> {\n return await f(this.value);\n }\n\n public catchError<TSuccess2, TError2>(\n _f: (error: never) => Result<TSuccess2, TError2>,\n ): Result<TSuccess1 | TSuccess2, TError2> {\n return this;\n }\n\n public async catchErrorAsync<TSuccess2, TError2>(\n _f: (error: never) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess1 | TSuccess2, TError2>> {\n return this;\n }\n\n public unwrapOrThrow(_errorCallback?: (e: never) => void): TSuccess1 {\n return this.value;\n }\n}\n\nclass ResultErrorImp<TError1> implements Result<never, TError1> {\n constructor(private error: TError1) {}\n\n public andThen<TSuccess2, TError2>(\n _f: (value: never) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError1 | TError2> {\n return this;\n }\n\n public async andThenAsync<TSuccess2, TError2>(\n _f: (value: never) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError1 | TError2>> {\n return this;\n }\n\n public catchError<TSuccess2, TError2>(\n f: (error: TError1) => Result<TSuccess2, TError2>,\n ): Result<TSuccess2, TError2> {\n return f(this.error);\n }\n\n public async catchErrorAsync<TSuccess2, TError2>(\n f: (error: TError1) => Promise<Result<TSuccess2, TError2>>,\n ): Promise<Result<TSuccess2, TError2>> {\n return await f(this.error);\n }\n\n public unwrapOrThrow(errorCallback?: (e: TError1) => void): never {\n if (errorCallback !== undefined) {\n errorCallback(this.error);\n }\n throw this.error;\n }\n}\n\nexport type UnwrapErrorFromResult<T> =\n T extends Result<any, infer U> ? U : never;\n\nexport function makeSafePromise<T>(\n promise: Promise<T>,\n): Promise<Result<T, ErrorUnknown>> {\n return promise\n .then((value) => resultSuccess(value))\n .catch((error) => resultError.unknown(error.message));\n}","import { ErrorType, Result, SuccessType } from \"./result\";\n\nexport function andThen<TSuccess, TError, TResult extends Result<any, any>>(\n callback: (value: TSuccess) => TResult\n): (\n result: Result<TSuccess, TError>\n) => Result<SuccessType<TResult>, TError | ErrorType<TResult>> {\n return (result) => result.andThen(callback);\n}\n\nexport function andThenAsync<\n TSuccess,\n TError,\n TResult extends Result<any, any>\n>(\n callback: (value: TSuccess) => Promise<TResult>\n): (\n result: Result<TSuccess, TError>\n) => Promise<Result<SuccessType<TResult>, TError | ErrorType<TResult>>> {\n return (result) => result.andThenAsync(callback);\n}\n\nexport function catchError<TSuccess, TError, TResult extends Result<any, any>>(\n callback: (error: TError) => TResult\n): (\n result: Result<TSuccess, TError>\n) => Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>> {\n return (result) => result.catchError(callback);\n}\n\nexport function catchErrorAsync<\n TSuccess,\n TError,\n TResult extends Result<any, any>\n>(\n callback: (error: TError) => Promise<TResult>\n): (\n result: Result<TSuccess, TError>\n) => Promise<Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>>> {\n return (result) => result.catchErrorAsync(callback);\n}\n"],"mappings":";AAgBO,SAAS,oBAAuB,QAA6B;AAClE,MAAI,kBAAkB,kBAAkB;AACtC,WAAO,OAAO,OAAO;AAAA,EACvB;AACA,QAAM,IAAI,MAAM,sCAAsC;AACxD;AAGO,SAAS,cACd,MACA,MAC6B;AAC7B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,YAAY,SAA+B;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,EAAE,QAAQ;AAAA,EAClB;AACF;AAOO,SAAS,oBAAgD;AAC9D,SAAO,IAAI,iBAA8B,MAAkB;AAC7D;AAEO,SAAS,cACd,OAC0B;AAC1B,SAAO,IAAI,iBAA4B,KAAK;AAC9C;AAIO,IAAM,cAAN,MAAkB;AAAA,EACvB,OAAc,QAAQ,SAA8C;AAClE,WAAO,IAAI,eAA6B,YAAY,OAAO,CAAC;AAAA,EAC9D;AAAA,EACA,OAAc,SACZ,MACA,OAAc,CAAC,GAC6B;AAC5C,WAAO,IAAI;AAAA,MACT,cAAc,MAAM,IAAI;AAAA,IAC1B;AAAA,EACF;AAAA,EACA,OAAc,UAEZ,OAAwC;AACxC,WAAO,IAAI,eAAwB,KAAK;AAAA,EAC1C;AACF;AAEA,IAAM,mBAAN,MAAsE;AAAA,EACpE,YAAoB,OAAkB;AAAlB;AAAA,EAAmB;AAAA,EAEhC,QACL,GAC4B;AAC5B,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAAA,EAEA,MAAa,aACX,GACqC;AACrC,WAAO,MAAM,EAAE,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEO,WACL,IACwC;AACxC,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,gBACX,IACiD;AACjD,WAAO;AAAA,EACT;AAAA,EAEO,cAAc,gBAAgD;AACnE,WAAO,KAAK;AAAA,EACd;AACF;AAEA,IAAM,iBAAN,MAAgE;AAAA,EAC9D,YAAoB,OAAgB;AAAhB;AAAA,EAAiB;AAAA,EAE9B,QACL,IACsC;AACtC,WAAO;AAAA,EACT;AAAA,EAEA,MAAa,aACX,IAC+C;AAC/C,WAAO;AAAA,EACT;AAAA,EAEO,WACL,GAC4B;AAC5B,WAAO,EAAE,KAAK,KAAK;AAAA,EACrB;AAAA,EAEA,MAAa,gBACX,GACqC;AACrC,WAAO,MAAM,EAAE,KAAK,KAAK;AAAA,EAC3B;AAAA,EAEO,cAAc,eAA6C;AAChE,QAAI,kBAAkB,QAAW;AAC/B,oBAAc,KAAK,KAAK;AAAA,IAC1B;AACA,UAAM,KAAK;AAAA,EACb;AACF;AAKO,SAAS,gBACd,SACkC;AAClC,SAAO,QACJ,KAAK,CAAC,UAAU,cAAc,KAAK,CAAC,EACpC,MAAM,CAAC,UAAU,YAAY,QAAQ,MAAM,OAAO,CAAC;AACxD;;;AC3JO,SAAS,QACd,UAG6D;AAC7D,SAAO,CAAC,WAAW,OAAO,QAAQ,QAAQ;AAC5C;AAEO,SAAS,aAKd,UAGsE;AACtE,SAAO,CAAC,WAAW,OAAO,aAAa,QAAQ;AACjD;AAEO,SAAS,WACd,UAG+D;AAC/D,SAAO,CAAC,WAAW,OAAO,WAAW,QAAQ;AAC/C;AAEO,SAAS,gBAKd,UAGwE;AACxE,SAAO,CAAC,WAAW,OAAO,gBAAgB,QAAQ;AACpD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@j2blasco/ts-result",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Result Monad for TypeScript",
|
|
5
|
-
"author": "j2blasco",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"keywords": [
|
|
8
|
-
"typescript",
|
|
9
|
-
"result",
|
|
10
|
-
"monad",
|
|
11
|
-
"error-handling"
|
|
12
|
-
],
|
|
13
|
-
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
15
|
-
},
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/j2blasco/ts-result.git"
|
|
19
|
-
},
|
|
20
|
-
"exports": {
|
|
21
|
-
".": {
|
|
22
|
-
"types": "./dist/index.d.ts",
|
|
23
|
-
"import": "./dist/index.mjs",
|
|
24
|
-
"require": "./dist/index.js"
|
|
25
|
-
}
|
|
26
|
-
},
|
|
27
|
-
"files": [
|
|
28
|
-
"dist",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
"@
|
|
42
|
-
"jest": "^29.
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@j2blasco/ts-result",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "Result Monad for TypeScript",
|
|
5
|
+
"author": "j2blasco",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"typescript",
|
|
9
|
+
"result",
|
|
10
|
+
"monad",
|
|
11
|
+
"error-handling"
|
|
12
|
+
],
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/j2blasco/ts-result.git"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.mjs",
|
|
24
|
+
"require": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"src",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "npm run clean && tsup",
|
|
35
|
+
"clean": "shx rm -rf dist",
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"test": "jest"
|
|
38
|
+
},
|
|
39
|
+
"type": "module",
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@j2blasco/ts-pipe": "^0.0.8",
|
|
42
|
+
"@types/jest": "^29.5.14",
|
|
43
|
+
"jest": "^29.7.0",
|
|
44
|
+
"shx": "^0.4.0",
|
|
45
|
+
"ts-jest": "^29.3.2",
|
|
46
|
+
"tsup": "^8.5.0",
|
|
47
|
+
"typescript": "~5.7.2"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// import { asyncPipe, pipe } from "@j2blasco/ts-pipe";
|
|
2
|
+
import { andThen, andThenAsync, catchError, catchErrorAsync } from "./result-functions";
|
|
3
|
+
import { resultSuccess, resultError, ErrorUnknown, Result } from "./result";
|
|
4
|
+
import { assertType } from "./utils/test/assert-type";
|
|
5
|
+
import { asyncPipe, pipe } from "@j2blasco/ts-pipe";
|
|
6
|
+
|
|
7
|
+
function doSomething(): Result<string, ErrorUnknown> {
|
|
8
|
+
return resultSuccess("5");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe("Result functions", () => {
|
|
12
|
+
test("andThen transforms success values", () => {
|
|
13
|
+
const result = resultSuccess(5);
|
|
14
|
+
const transformed = andThen((x: number) => resultSuccess(x * 2))(result);
|
|
15
|
+
expect(transformed.unwrapOrThrow()).toBe(10);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("andThen preserves failures", () => {
|
|
19
|
+
const result = resultError.unknown("error message");
|
|
20
|
+
const transformed = andThen((x: any) => resultSuccess(x * 2))(result);
|
|
21
|
+
|
|
22
|
+
// Check if the transformation preserves the error by trying to unwrap
|
|
23
|
+
expect(() => transformed.unwrapOrThrow()).toThrow();
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("andThenAsync transforms success values", async () => {
|
|
27
|
+
const result = resultSuccess(5);
|
|
28
|
+
const transformed = await andThenAsync(async (x: number) =>
|
|
29
|
+
resultSuccess(x * 2)
|
|
30
|
+
)(result);
|
|
31
|
+
expect(transformed.unwrapOrThrow()).toBe(10);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("andThenAsync preserves failures", async () => {
|
|
35
|
+
const result = resultError.unknown("error message");
|
|
36
|
+
const transformed = await andThenAsync(async (x: any) =>
|
|
37
|
+
resultSuccess(x * 2)
|
|
38
|
+
)(result);
|
|
39
|
+
|
|
40
|
+
// Check if the transformation preserves the error by trying to unwrap
|
|
41
|
+
expect(() => transformed.unwrapOrThrow()).toThrow();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("catchError handles errors", () => {
|
|
45
|
+
const result = resultError.unknown("error message");
|
|
46
|
+
const recovered = catchError((error: ErrorUnknown) =>
|
|
47
|
+
resultSuccess("recovered")
|
|
48
|
+
)(result);
|
|
49
|
+
expect(recovered.unwrapOrThrow()).toBe("recovered");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("catchError preserves success values", () => {
|
|
53
|
+
const result = resultSuccess(42);
|
|
54
|
+
const recovered = catchError((error: any) =>
|
|
55
|
+
resultSuccess("should not be called")
|
|
56
|
+
)(result);
|
|
57
|
+
expect(recovered.unwrapOrThrow()).toBe(42);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("catchErrorAsync handles errors", async () => {
|
|
61
|
+
const result = resultError.unknown("error message");
|
|
62
|
+
const recovered = await catchErrorAsync(async (error: ErrorUnknown) =>
|
|
63
|
+
resultSuccess("recovered async")
|
|
64
|
+
)(result);
|
|
65
|
+
expect(recovered.unwrapOrThrow()).toBe("recovered async");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("catchErrorAsync preserves success values", async () => {
|
|
69
|
+
const result = resultSuccess(42);
|
|
70
|
+
const recovered = await catchErrorAsync(async (error: any) =>
|
|
71
|
+
resultSuccess("should not be called")
|
|
72
|
+
)(result);
|
|
73
|
+
expect(recovered.unwrapOrThrow()).toBe(42);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe("Result integration with ts-pipe", () => {
|
|
78
|
+
test("asyncPipe", async () => {
|
|
79
|
+
var result = await asyncPipe(
|
|
80
|
+
doSomething(),
|
|
81
|
+
andThenAsync(async (_e) => resultSuccess(new Date())),
|
|
82
|
+
andThen((r) => {
|
|
83
|
+
assertType<typeof r, Date>(true);
|
|
84
|
+
return resultSuccess("test");
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
expect(result.unwrapOrThrow()).toBe("test");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("pipe", async () => {
|
|
91
|
+
var result = pipe(
|
|
92
|
+
doSomething(),
|
|
93
|
+
andThen((_e) => resultSuccess(new Date())),
|
|
94
|
+
andThen((r) => {
|
|
95
|
+
assertType<typeof r, Date>(true);
|
|
96
|
+
return resultSuccess("test");
|
|
97
|
+
})
|
|
98
|
+
);
|
|
99
|
+
expect(result.unwrapOrThrow()).toBe("test");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("pipe", async () => {
|
|
103
|
+
var result = pipe(
|
|
104
|
+
doSomething(),
|
|
105
|
+
andThen((_e) => resultSuccess(new Date())),
|
|
106
|
+
andThen((r) => {
|
|
107
|
+
assertType<typeof r, Date>(true);
|
|
108
|
+
return resultSuccess("test");
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
expect(result.unwrapOrThrow()).toBe("test");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ErrorType, Result, SuccessType } from "./result";
|
|
2
|
+
|
|
3
|
+
export function andThen<TSuccess, TError, TResult extends Result<any, any>>(
|
|
4
|
+
callback: (value: TSuccess) => TResult
|
|
5
|
+
): (
|
|
6
|
+
result: Result<TSuccess, TError>
|
|
7
|
+
) => Result<SuccessType<TResult>, TError | ErrorType<TResult>> {
|
|
8
|
+
return (result) => result.andThen(callback);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function andThenAsync<
|
|
12
|
+
TSuccess,
|
|
13
|
+
TError,
|
|
14
|
+
TResult extends Result<any, any>
|
|
15
|
+
>(
|
|
16
|
+
callback: (value: TSuccess) => Promise<TResult>
|
|
17
|
+
): (
|
|
18
|
+
result: Result<TSuccess, TError>
|
|
19
|
+
) => Promise<Result<SuccessType<TResult>, TError | ErrorType<TResult>>> {
|
|
20
|
+
return (result) => result.andThenAsync(callback);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function catchError<TSuccess, TError, TResult extends Result<any, any>>(
|
|
24
|
+
callback: (error: TError) => TResult
|
|
25
|
+
): (
|
|
26
|
+
result: Result<TSuccess, TError>
|
|
27
|
+
) => Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>> {
|
|
28
|
+
return (result) => result.catchError(callback);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function catchErrorAsync<
|
|
32
|
+
TSuccess,
|
|
33
|
+
TError,
|
|
34
|
+
TResult extends Result<any, any>
|
|
35
|
+
>(
|
|
36
|
+
callback: (error: TError) => Promise<TResult>
|
|
37
|
+
): (
|
|
38
|
+
result: Result<TSuccess, TError>
|
|
39
|
+
) => Promise<Result<TSuccess | SuccessType<TResult>, ErrorType<TResult>>> {
|
|
40
|
+
return (result) => result.catchErrorAsync(callback);
|
|
41
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resultSuccess,
|
|
3
|
+
resultError,
|
|
4
|
+
unwrapSuccessResult,
|
|
5
|
+
Result,
|
|
6
|
+
ErrorWithCode,
|
|
7
|
+
resultSuccessVoid,
|
|
8
|
+
SuccessVoid,
|
|
9
|
+
ErrorUnknown,
|
|
10
|
+
} from "./result";
|
|
11
|
+
import { assertType, expectToCompile } from "./utils/test/assert-type";
|
|
12
|
+
|
|
13
|
+
function createResult<TSuccess, TError>(): Result<TSuccess, TError> {
|
|
14
|
+
return resultSuccessVoid() as object as Result<TSuccess, TError>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
describe("Result Monad", () => {
|
|
18
|
+
test("resultSuccess should create a success result", () => {
|
|
19
|
+
const success = resultSuccess("test");
|
|
20
|
+
expect(unwrapSuccessResult(success)).toBe("test");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("resultError.unknown should create an unknown error result", () => {
|
|
24
|
+
const error = resultError.unknown("An error occurred");
|
|
25
|
+
try {
|
|
26
|
+
error.unwrapOrThrow();
|
|
27
|
+
} catch (e) {
|
|
28
|
+
expect(e).toMatchObject({
|
|
29
|
+
code: "unknown",
|
|
30
|
+
data: { message: "An error occurred" },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("resultError.withCode should create a coded error result", () => {
|
|
36
|
+
const error = resultError.withCode("test-code" as const, {
|
|
37
|
+
detail: "Some details",
|
|
38
|
+
});
|
|
39
|
+
try {
|
|
40
|
+
error.unwrapOrThrow();
|
|
41
|
+
} catch (e) {
|
|
42
|
+
expect(e).toMatchObject({
|
|
43
|
+
code: "test-code" as const,
|
|
44
|
+
data: { detail: "Some details" },
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("Result Monad success should be chainable with inferred types - string -> int", () => {
|
|
50
|
+
const resultString = createResult<string, ErrorWithCode<"test">>();
|
|
51
|
+
|
|
52
|
+
const resultInt = resultString.andThen((value) => {
|
|
53
|
+
type Expected = string;
|
|
54
|
+
assertType<typeof value, Expected>(true);
|
|
55
|
+
return resultSuccess(42);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
type Expected = Result<number, ErrorWithCode<"test">>;
|
|
59
|
+
assertType<typeof resultInt, Expected>(true);
|
|
60
|
+
|
|
61
|
+
expectToCompile();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("Result Monad success should be chainable with inferred types - obj -> obj", () => {
|
|
65
|
+
const resultString = createResult<
|
|
66
|
+
{ test: string },
|
|
67
|
+
ErrorWithCode<"test">
|
|
68
|
+
>();
|
|
69
|
+
|
|
70
|
+
const resultInt = resultString.andThen((value) => {
|
|
71
|
+
type Expected = { test: string };
|
|
72
|
+
assertType<typeof value, Expected>(true);
|
|
73
|
+
return resultSuccess({ test2: "test" });
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
type Expected = Result<{ test2: string }, ErrorWithCode<"test">>;
|
|
77
|
+
assertType<typeof resultInt, Expected>(true);
|
|
78
|
+
|
|
79
|
+
expectToCompile();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("Type is properly infered in resultError.fromError (simple case)", () => {
|
|
83
|
+
const result = createResult<
|
|
84
|
+
SuccessVoid,
|
|
85
|
+
ErrorWithCode<"error-code"> | ErrorUnknown
|
|
86
|
+
>();
|
|
87
|
+
const catchedResult = result.catchError((e) => {
|
|
88
|
+
if (e.code === "error-code") {
|
|
89
|
+
return resultSuccessVoid();
|
|
90
|
+
}
|
|
91
|
+
return resultError.fromError(e);
|
|
92
|
+
});
|
|
93
|
+
type Expected = Result<never, ErrorUnknown>;
|
|
94
|
+
assertType<typeof catchedResult, Expected>(true);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("Type is properly infered in resultError.fromError (complex case)", () => {
|
|
98
|
+
const result = createResult<
|
|
99
|
+
SuccessVoid,
|
|
100
|
+
| ErrorWithCode<"error-code-1">
|
|
101
|
+
| ErrorWithCode<"error-code-2">
|
|
102
|
+
| ErrorUnknown
|
|
103
|
+
>();
|
|
104
|
+
const catchedResult = result.catchError((e) => {
|
|
105
|
+
if (e.code === "error-code-1") {
|
|
106
|
+
return resultSuccess(5);
|
|
107
|
+
}
|
|
108
|
+
return resultError.fromError(e);
|
|
109
|
+
});
|
|
110
|
+
type Expected = Result<number, ErrorUnknown | ErrorWithCode<'error-code-2'>>;
|
|
111
|
+
assertType<typeof catchedResult, Expected>(true);
|
|
112
|
+
});
|
|
113
|
+
});
|
package/src/result.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
export interface Result<TSuccess1, TError1> {
|
|
2
|
+
andThen<TResult extends Result<any, any>>(
|
|
3
|
+
f: (value: TSuccess1) => TResult,
|
|
4
|
+
): Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>;
|
|
5
|
+
andThenAsync<TResult extends Result<any, any>>(
|
|
6
|
+
f: (value: TSuccess1) => Promise<TResult>,
|
|
7
|
+
): Promise<Result<SuccessType<TResult>, TError1 | ErrorType<TResult>>>;
|
|
8
|
+
catchError<TResult extends Result<any, any>>(
|
|
9
|
+
f: (error: TError1) => TResult,
|
|
10
|
+
): Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>;
|
|
11
|
+
catchErrorAsync<TResult extends Result<any, any>>(
|
|
12
|
+
f: (error: TError1) => Promise<TResult>,
|
|
13
|
+
): Promise<Result<TSuccess1 | SuccessType<TResult>, ErrorType<TResult>>>;
|
|
14
|
+
unwrapOrThrow(errorCallback?: (e: TError1) => void): TSuccess1;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function unwrapSuccessResult<T>(result: Result<T, never>): T {
|
|
18
|
+
if (result instanceof ResultSuccessImp) {
|
|
19
|
+
return result['value'];
|
|
20
|
+
}
|
|
21
|
+
throw new Error('Unreachable code. Result is an error');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type ErrorWithCode<TCode, TData = object> = { code: TCode; data: TData };
|
|
25
|
+
export function errorWithCode<TCode, TData>(
|
|
26
|
+
code: TCode,
|
|
27
|
+
data: TData,
|
|
28
|
+
): ErrorWithCode<TCode, TData> {
|
|
29
|
+
return {
|
|
30
|
+
code,
|
|
31
|
+
data,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ErrorUnknown = {
|
|
36
|
+
code: 'unknown';
|
|
37
|
+
data: { message: string };
|
|
38
|
+
};
|
|
39
|
+
export function errorUnkown(message: string): ErrorUnknown {
|
|
40
|
+
return {
|
|
41
|
+
code: 'unknown',
|
|
42
|
+
data: { message },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type SuccessType<T> = T extends Result<infer S, any> ? S : never;
|
|
47
|
+
export type ErrorType<T> = T extends Result<any, infer E> ? E : never;
|
|
48
|
+
|
|
49
|
+
export type SuccessVoid = never;
|
|
50
|
+
|
|
51
|
+
export function resultSuccessVoid(): Result<SuccessVoid, never> {
|
|
52
|
+
return new ResultSuccessImp<SuccessVoid>(undefined as never);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function resultSuccess<TSuccess1>(
|
|
56
|
+
value: TSuccess1,
|
|
57
|
+
): Result<TSuccess1, never> {
|
|
58
|
+
return new ResultSuccessImp<TSuccess1>(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type EnforceLiteral<T> = string extends T ? never : T;
|
|
62
|
+
|
|
63
|
+
export class resultError {
|
|
64
|
+
public static unknown(message: string): Result<never, ErrorUnknown> {
|
|
65
|
+
return new ResultErrorImp<ErrorUnknown>(errorUnkown(message));
|
|
66
|
+
}
|
|
67
|
+
public static withCode<TCode, TData = object>(
|
|
68
|
+
code: EnforceLiteral<TCode>,
|
|
69
|
+
data: TData = {} as TData,
|
|
70
|
+
): Result<never, ErrorWithCode<TCode, TData>> {
|
|
71
|
+
return new ResultErrorImp<ErrorWithCode<TCode, TData>>(
|
|
72
|
+
errorWithCode(code, data),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
public static fromError<
|
|
76
|
+
TError1 extends ErrorWithCode<any, object>,
|
|
77
|
+
>(error: TError1): Result<never, TError1> {
|
|
78
|
+
return new ResultErrorImp<TError1>(error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
class ResultSuccessImp<TSuccess1> implements Result<TSuccess1, never> {
|
|
83
|
+
constructor(private value: TSuccess1) {}
|
|
84
|
+
|
|
85
|
+
public andThen<TSuccess2, TError2>(
|
|
86
|
+
f: (value: TSuccess1) => Result<TSuccess2, TError2>,
|
|
87
|
+
): Result<TSuccess2, TError2> {
|
|
88
|
+
return f(this.value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async andThenAsync<TSuccess2, TError2>(
|
|
92
|
+
f: (value: TSuccess1) => Promise<Result<TSuccess2, TError2>>,
|
|
93
|
+
): Promise<Result<TSuccess2, TError2>> {
|
|
94
|
+
return await f(this.value);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public catchError<TSuccess2, TError2>(
|
|
98
|
+
_f: (error: never) => Result<TSuccess2, TError2>,
|
|
99
|
+
): Result<TSuccess1 | TSuccess2, TError2> {
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
public async catchErrorAsync<TSuccess2, TError2>(
|
|
104
|
+
_f: (error: never) => Promise<Result<TSuccess2, TError2>>,
|
|
105
|
+
): Promise<Result<TSuccess1 | TSuccess2, TError2>> {
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public unwrapOrThrow(_errorCallback?: (e: never) => void): TSuccess1 {
|
|
110
|
+
return this.value;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
class ResultErrorImp<TError1> implements Result<never, TError1> {
|
|
115
|
+
constructor(private error: TError1) {}
|
|
116
|
+
|
|
117
|
+
public andThen<TSuccess2, TError2>(
|
|
118
|
+
_f: (value: never) => Result<TSuccess2, TError2>,
|
|
119
|
+
): Result<TSuccess2, TError1 | TError2> {
|
|
120
|
+
return this;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public async andThenAsync<TSuccess2, TError2>(
|
|
124
|
+
_f: (value: never) => Promise<Result<TSuccess2, TError2>>,
|
|
125
|
+
): Promise<Result<TSuccess2, TError1 | TError2>> {
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public catchError<TSuccess2, TError2>(
|
|
130
|
+
f: (error: TError1) => Result<TSuccess2, TError2>,
|
|
131
|
+
): Result<TSuccess2, TError2> {
|
|
132
|
+
return f(this.error);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public async catchErrorAsync<TSuccess2, TError2>(
|
|
136
|
+
f: (error: TError1) => Promise<Result<TSuccess2, TError2>>,
|
|
137
|
+
): Promise<Result<TSuccess2, TError2>> {
|
|
138
|
+
return await f(this.error);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public unwrapOrThrow(errorCallback?: (e: TError1) => void): never {
|
|
142
|
+
if (errorCallback !== undefined) {
|
|
143
|
+
errorCallback(this.error);
|
|
144
|
+
}
|
|
145
|
+
throw this.error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export type UnwrapErrorFromResult<T> =
|
|
150
|
+
T extends Result<any, infer U> ? U : never;
|
|
151
|
+
|
|
152
|
+
export function makeSafePromise<T>(
|
|
153
|
+
promise: Promise<T>,
|
|
154
|
+
): Promise<Result<T, ErrorUnknown>> {
|
|
155
|
+
return promise
|
|
156
|
+
.then((value) => resultSuccess(value))
|
|
157
|
+
.catch((error) => resultError.unknown(error.message));
|
|
158
|
+
}
|