go-go-try 4.1.2 → 6.0.0

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Alisson Cavalcante Agiani thelinuxlich@gmail.com https://astoilkov.com
3
+ Copyright (c) 2025 Alisson Cavalcante Agiani thelinuxlich@gmail.com
4
+ Copyright (c) 2022 Antonio Stoilkov hello@astoilkov.com https://astoilkov.com
4
5
 
5
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
7
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -4,12 +4,11 @@
4
4
 
5
5
  ## Why
6
6
 
7
- Why not [`nice-try`](https://github.com/electerious/nice-try) with it's 70+ million downloads per month?
8
-
9
- - `go-go-try` supports async functions.
10
- - `go-go-try` allows you to capture the thrown error.
11
- - `go-go-try` is written in TypeScript. The types are written in a way that reduce developer errors.
12
- - `go-go-try` is inspired by Golang error catching.
7
+ - Supports sync/async functions.
8
+ - Allows you to capture the thrown error.
9
+ - Written in TypeScript. The types are written in a way that reduce developer errors.
10
+ - Inspired by Golang error catching.
11
+ - Zero dependencies.
13
12
 
14
13
  Why not just `try`/`catch`?
15
14
 
@@ -34,7 +33,7 @@ npm install go-go-try
34
33
  ## Usage
35
34
 
36
35
  ```ts
37
- import { goTry, goExpect, goTryRaw } from 'go-go-try'
36
+ import { goTry, goTryRaw } from 'go-go-try'
38
37
 
39
38
  // tries to parse todos, returns empty array if it fails
40
39
  const [_, value = []] = goTry(() => JSON.parse(todos))
@@ -43,25 +42,26 @@ const [_, value = []] = goTry(() => JSON.parse(todos))
43
42
  const [_, todos = []] = await goTry(fetchTodos())
44
43
 
45
44
  // fetch todos, fallback to empty array, send error to your error tracking service
46
- const [err, todos = []] = await goTry(fetchTodos())
47
- sentToErrorTrackingService(err)
45
+ const [err, todos = []] = await goTry(fetchTodos()) // err is string | undefined
46
+ if (err) sendToErrorTrackingService(err)
48
47
 
49
- // if you still want the task to throw but with a optional custom message
50
- const value = await goExpect(() => JSON.parse('{/}', (err) => `Malformed JSON!`)) // value will be always T
48
+ // goTry extracts the error message from the error object, if you want the raw error object, use goTryRaw
49
+ const [err, value] = goTryRaw<Error>(() => JSON.parse('{/}')) // err will be unknown, value will be always T and you can add a Error type as the first generic argument to avoid checking `instanceof Error`
51
50
 
52
- // goTry and goExpect extract the error message from the error object, if you want the raw error object, use goTryRaw
53
- const [err, value] = await goTryRaw(() => JSON.parse('{/}')) // err will be unknown, value will be always T
51
+ // fetch todos, fallback to empty array, send error to your error tracking service
52
+ const [err, todos = []] = await goTryRaw<SomeErrorType>(fetchTodos()) // err is SomeErrorType | undefined
53
+ if (err) sendToErrorTrackingService(err)
54
54
  ```
55
55
 
56
56
  ## API
57
57
 
58
58
  **First parameter** accepts:
59
59
 
60
- - synchronous function `goTry(() => JSON.parse(value))`
61
- - asynchronous function / Promise
60
+ - synchronous/asynchronous function / Promise
62
61
 
63
62
  **Returns** a tuple with the possible error and result (Golang style)
64
63
 
64
+
65
65
  If you use TypeScript, the types are well defined and won't let you make a mistake.
66
66
 
67
67
  ## Inspiration
package/dist/index.cjs CHANGED
@@ -1,67 +1,57 @@
1
1
  'use strict';
2
2
 
3
- var pIsPromise = require('p-is-promise');
4
-
5
- function isErrorWithMessage(error) {
6
- return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
3
+ function isSuccess(result) {
4
+ return result[0] === void 0;
5
+ }
6
+ function isFailure(result) {
7
+ return result[0] !== void 0;
8
+ }
9
+ function success(value) {
10
+ return [void 0, value];
7
11
  }
8
- function toErrorWithMessage(maybeError) {
9
- if (isErrorWithMessage(maybeError)) {
10
- return maybeError;
12
+ function failure(error) {
13
+ return [error, void 0];
14
+ }
15
+ function getErrorMessage(error) {
16
+ if (typeof error === "string") return error;
17
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
18
+ return error.message;
11
19
  }
12
20
  try {
13
- return new Error(JSON.stringify(maybeError));
21
+ return JSON.stringify(error);
14
22
  } catch {
15
- return new Error(String(maybeError));
23
+ return String(error);
16
24
  }
17
25
  }
18
- function getErrorMessage(error) {
19
- return toErrorWithMessage(error).message;
20
- }
21
- function isPromise(p) {
22
- return pIsPromise(p);
26
+ function isPromiseLike(value) {
27
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
23
28
  }
24
29
  function goTry(value) {
25
- let unwrappedValue;
30
+ if (isPromiseLike(value)) {
31
+ return Promise.resolve(value).then((value2) => success(value2)).catch((err) => failure(getErrorMessage(err)));
32
+ }
26
33
  try {
27
- unwrappedValue = typeof value === "function" ? value() : value;
28
- if (isPromise(unwrappedValue)) {
29
- return Promise.resolve(unwrappedValue).then((value2) => [void 0, value2]).catch((err) => [getErrorMessage(err), void 0]);
30
- }
31
- return [void 0, unwrappedValue];
34
+ const result = typeof value === "function" ? value() : value;
35
+ return success(result);
32
36
  } catch (err) {
33
- return [getErrorMessage(err), void 0];
37
+ return failure(getErrorMessage(err));
34
38
  }
35
39
  }
36
40
  function goTryRaw(value) {
37
- let unwrappedValue;
38
- try {
39
- unwrappedValue = typeof value === "function" ? value() : value;
40
- if (isPromise(unwrappedValue)) {
41
- return Promise.resolve(unwrappedValue).then((value2) => [void 0, value2]).catch((err) => [err, void 0]);
42
- }
43
- return [void 0, unwrappedValue];
44
- } catch (err) {
45
- return [err, void 0];
41
+ if (isPromiseLike(value)) {
42
+ return Promise.resolve(value).then((value2) => success(value2)).catch((err) => failure(err));
46
43
  }
47
- }
48
- async function goExpect(value, error) {
49
- const _error = error ?? ((e) => e);
50
44
  try {
51
- const unwrappedValue = typeof value === "function" ? value() : value;
52
- if (isPromise(unwrappedValue)) {
53
- const [err, res] = await goTry(unwrappedValue);
54
- if (err !== void 0) {
55
- throw new Error(err);
56
- }
57
- return res;
58
- }
59
- return unwrappedValue;
45
+ const result = typeof value === "function" ? value() : value;
46
+ return success(result);
60
47
  } catch (err) {
61
- throw new Error(_error(getErrorMessage(err)));
48
+ return failure(err);
62
49
  }
63
50
  }
64
51
 
65
- exports.goExpect = goExpect;
52
+ exports.failure = failure;
66
53
  exports.goTry = goTry;
67
54
  exports.goTryRaw = goTryRaw;
55
+ exports.isFailure = isFailure;
56
+ exports.isSuccess = isSuccess;
57
+ exports.success = success;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,54 @@
1
- type ResultTuple<T> = readonly [undefined, T] | readonly [string, undefined];
2
- type RawResultTuple<T, E = unknown> = readonly [undefined, T] | readonly [E, undefined];
3
- declare function goTry<T>(value: PromiseLike<T>): PromiseLike<ResultTuple<T>>;
4
- declare function goTry<T>(value: () => T): ResultTuple<T>;
5
- declare function goTryRaw<T, E = unknown>(value: PromiseLike<T>): PromiseLike<RawResultTuple<T, E>>;
6
- declare function goTryRaw<T, E = unknown>(value: () => T): RawResultTuple<T, E>;
7
- declare function goExpect<T>(value: (() => T) | PromiseLike<T>, error?: (err: string) => string): Promise<T>;
1
+ /**
2
+ * Type for a successful result
3
+ */
4
+ type Success<T> = readonly [undefined, T];
5
+ /**
6
+ * Type for an error result
7
+ */
8
+ type Failure<E> = readonly [E, undefined];
9
+ /**
10
+ * Type for a result that can be either success or failure
11
+ */
12
+ type Result<E, T> = Success<T> | Failure<E>;
13
+ /**
14
+ * Type guard to check if a result is a success
15
+ */
16
+ declare function isSuccess<E, T>(result: Result<E, T>): result is Success<T>;
17
+ /**
18
+ * Type guard to check if a result is a failure
19
+ */
20
+ declare function isFailure<E, T>(result: Result<E, T>): result is Failure<E>;
21
+ /**
22
+ * Helper to create a success result
23
+ */
24
+ declare function success<T>(value: T): Success<T>;
25
+ /**
26
+ * Helper to create a failure result
27
+ */
28
+ declare function failure<E>(error: E): Failure<E>;
29
+ /**
30
+ * Executes a synchronous function and returns a Result
31
+ */
32
+ declare function goTry<T>(fn: () => T): Result<string, T>;
33
+ /**
34
+ * Handles a value directly and returns a Result
35
+ */
36
+ declare function goTry<T>(value: T): Result<string, T>;
37
+ /**
38
+ * Handles a promise and returns a Promise<Result>
39
+ */
40
+ declare function goTry<T>(promise: PromiseLike<T>): Promise<Result<string, T>>;
41
+ /**
42
+ * Executes a synchronous function and returns a Result with the raw error
43
+ */
44
+ declare function goTryRaw<E = unknown, T = unknown>(fn: () => T): Result<E, T>;
45
+ /**
46
+ * Handles a value directly and returns a Result
47
+ */
48
+ declare function goTryRaw<E = unknown, T = unknown>(value: T): Result<E, T>;
49
+ /**
50
+ * Handles a promise and returns a Promise<Result> with the raw error
51
+ */
52
+ declare function goTryRaw<E = unknown, T = unknown>(promise: PromiseLike<T>): Promise<Result<E, T>>;
8
53
 
9
- export { goExpect, goTry, goTryRaw };
54
+ export { type Failure, type Result, type Success, failure, goTry, goTryRaw, isFailure, isSuccess, success };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,54 @@
1
- type ResultTuple<T> = readonly [undefined, T] | readonly [string, undefined];
2
- type RawResultTuple<T, E = unknown> = readonly [undefined, T] | readonly [E, undefined];
3
- declare function goTry<T>(value: PromiseLike<T>): PromiseLike<ResultTuple<T>>;
4
- declare function goTry<T>(value: () => T): ResultTuple<T>;
5
- declare function goTryRaw<T, E = unknown>(value: PromiseLike<T>): PromiseLike<RawResultTuple<T, E>>;
6
- declare function goTryRaw<T, E = unknown>(value: () => T): RawResultTuple<T, E>;
7
- declare function goExpect<T>(value: (() => T) | PromiseLike<T>, error?: (err: string) => string): Promise<T>;
1
+ /**
2
+ * Type for a successful result
3
+ */
4
+ type Success<T> = readonly [undefined, T];
5
+ /**
6
+ * Type for an error result
7
+ */
8
+ type Failure<E> = readonly [E, undefined];
9
+ /**
10
+ * Type for a result that can be either success or failure
11
+ */
12
+ type Result<E, T> = Success<T> | Failure<E>;
13
+ /**
14
+ * Type guard to check if a result is a success
15
+ */
16
+ declare function isSuccess<E, T>(result: Result<E, T>): result is Success<T>;
17
+ /**
18
+ * Type guard to check if a result is a failure
19
+ */
20
+ declare function isFailure<E, T>(result: Result<E, T>): result is Failure<E>;
21
+ /**
22
+ * Helper to create a success result
23
+ */
24
+ declare function success<T>(value: T): Success<T>;
25
+ /**
26
+ * Helper to create a failure result
27
+ */
28
+ declare function failure<E>(error: E): Failure<E>;
29
+ /**
30
+ * Executes a synchronous function and returns a Result
31
+ */
32
+ declare function goTry<T>(fn: () => T): Result<string, T>;
33
+ /**
34
+ * Handles a value directly and returns a Result
35
+ */
36
+ declare function goTry<T>(value: T): Result<string, T>;
37
+ /**
38
+ * Handles a promise and returns a Promise<Result>
39
+ */
40
+ declare function goTry<T>(promise: PromiseLike<T>): Promise<Result<string, T>>;
41
+ /**
42
+ * Executes a synchronous function and returns a Result with the raw error
43
+ */
44
+ declare function goTryRaw<E = unknown, T = unknown>(fn: () => T): Result<E, T>;
45
+ /**
46
+ * Handles a value directly and returns a Result
47
+ */
48
+ declare function goTryRaw<E = unknown, T = unknown>(value: T): Result<E, T>;
49
+ /**
50
+ * Handles a promise and returns a Promise<Result> with the raw error
51
+ */
52
+ declare function goTryRaw<E = unknown, T = unknown>(promise: PromiseLike<T>): Promise<Result<E, T>>;
8
53
 
9
- export { goExpect, goTry, goTryRaw };
54
+ export { type Failure, type Result, type Success, failure, goTry, goTryRaw, isFailure, isSuccess, success };
package/dist/index.mjs CHANGED
@@ -1,63 +1,50 @@
1
- import pIsPromise from 'p-is-promise';
2
-
3
- function isErrorWithMessage(error) {
4
- return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
1
+ function isSuccess(result) {
2
+ return result[0] === void 0;
3
+ }
4
+ function isFailure(result) {
5
+ return result[0] !== void 0;
6
+ }
7
+ function success(value) {
8
+ return [void 0, value];
5
9
  }
6
- function toErrorWithMessage(maybeError) {
7
- if (isErrorWithMessage(maybeError)) {
8
- return maybeError;
10
+ function failure(error) {
11
+ return [error, void 0];
12
+ }
13
+ function getErrorMessage(error) {
14
+ if (typeof error === "string") return error;
15
+ if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
16
+ return error.message;
9
17
  }
10
18
  try {
11
- return new Error(JSON.stringify(maybeError));
19
+ return JSON.stringify(error);
12
20
  } catch {
13
- return new Error(String(maybeError));
21
+ return String(error);
14
22
  }
15
23
  }
16
- function getErrorMessage(error) {
17
- return toErrorWithMessage(error).message;
18
- }
19
- function isPromise(p) {
20
- return pIsPromise(p);
24
+ function isPromiseLike(value) {
25
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
21
26
  }
22
27
  function goTry(value) {
23
- let unwrappedValue;
28
+ if (isPromiseLike(value)) {
29
+ return Promise.resolve(value).then((value2) => success(value2)).catch((err) => failure(getErrorMessage(err)));
30
+ }
24
31
  try {
25
- unwrappedValue = typeof value === "function" ? value() : value;
26
- if (isPromise(unwrappedValue)) {
27
- return Promise.resolve(unwrappedValue).then((value2) => [void 0, value2]).catch((err) => [getErrorMessage(err), void 0]);
28
- }
29
- return [void 0, unwrappedValue];
32
+ const result = typeof value === "function" ? value() : value;
33
+ return success(result);
30
34
  } catch (err) {
31
- return [getErrorMessage(err), void 0];
35
+ return failure(getErrorMessage(err));
32
36
  }
33
37
  }
34
38
  function goTryRaw(value) {
35
- let unwrappedValue;
36
- try {
37
- unwrappedValue = typeof value === "function" ? value() : value;
38
- if (isPromise(unwrappedValue)) {
39
- return Promise.resolve(unwrappedValue).then((value2) => [void 0, value2]).catch((err) => [err, void 0]);
40
- }
41
- return [void 0, unwrappedValue];
42
- } catch (err) {
43
- return [err, void 0];
39
+ if (isPromiseLike(value)) {
40
+ return Promise.resolve(value).then((value2) => success(value2)).catch((err) => failure(err));
44
41
  }
45
- }
46
- async function goExpect(value, error) {
47
- const _error = error ?? ((e) => e);
48
42
  try {
49
- const unwrappedValue = typeof value === "function" ? value() : value;
50
- if (isPromise(unwrappedValue)) {
51
- const [err, res] = await goTry(unwrappedValue);
52
- if (err !== void 0) {
53
- throw new Error(err);
54
- }
55
- return res;
56
- }
57
- return unwrappedValue;
43
+ const result = typeof value === "function" ? value() : value;
44
+ return success(result);
58
45
  } catch (err) {
59
- throw new Error(_error(getErrorMessage(err)));
46
+ return failure(err);
60
47
  }
61
48
  }
62
49
 
63
- export { goExpect, goTry, goTryRaw };
50
+ export { failure, goTry, goTryRaw, isFailure, isSuccess, success };
package/package.json CHANGED
@@ -1,59 +1,50 @@
1
1
  {
2
- "name": "go-go-try",
3
- "version": "4.1.2",
4
- "description": "Tries to execute a sync/async function, returns a result tuple",
5
- "license": "MIT",
6
- "repository": "thelinuxlich/go-go-try",
7
- "author": {
8
- "name": "Alisson Cavalcante Agiani",
9
- "email": "thelinuxlich@gmail.com"
10
- },
11
- "type": "module",
12
- "main": "./dist/index.cjs",
13
- "module": "./dist/index.mjs",
14
- "types": "./dist/index.d.ts",
15
- "exports": {
16
- "require": {
17
- "types": "./dist/index.d.cts",
18
- "default": "./dist/index.cjs"
19
- },
20
- "import": {
21
- "types": "./dist/index.d.mts",
22
- "default": "./dist/index.mjs"
23
- }
24
- },
25
- "sideEffects": false,
26
- "engines": {
27
- "node": ">=12"
28
- },
29
- "scripts": {
30
- "build": "npx pkgroll",
31
- "lint": "biome lint --apply src/*.ts",
32
- "test": "yarn run build && yarn run lint && NODE_OPTIONS=--experimental-vm-modules jest --coverage --coverageReporters=text"
33
- },
34
- "keywords": [
35
- "errors",
36
- "try",
37
- "catch",
38
- "error handling",
39
- "nice-try",
40
- "good-try",
41
- "go-go-try",
42
- "gotry",
43
- "go-try"
44
- ],
45
- "devDependencies": {
46
- "@biomejs/biome": "^1.7.0",
47
- "@types/jest": "^28.1.1",
48
- "confusing-browser-globals": "^1.0.11",
49
- "jest": "^28.1.1",
50
- "jest-environment-jsdom": "^28.1.1",
51
- "np": "^7.6.1",
52
- "ts-jest": "^28.0.4",
53
- "ts-node": "^10.8.2",
54
- "typescript": "^5.4.5"
55
- },
56
- "dependencies": {
57
- "p-is-promise": "^4.0.0"
58
- }
59
- }
2
+ "name": "go-go-try",
3
+ "version": "6.0.0",
4
+ "description": "Tries to execute a sync/async function, returns a result tuple",
5
+ "license": "MIT",
6
+ "repository": "thelinuxlich/go-go-try",
7
+ "author": {
8
+ "name": "Alisson Cavalcante Agiani",
9
+ "email": "thelinuxlich@gmail.com"
10
+ },
11
+ "type": "module",
12
+ "main": "./dist/index.cjs",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "exports": {
16
+ "require": {
17
+ "types": "./dist/index.d.cts",
18
+ "default": "./dist/index.cjs"
19
+ },
20
+ "import": {
21
+ "types": "./dist/index.d.mts",
22
+ "default": "./dist/index.mjs"
23
+ }
24
+ },
25
+ "engines": {
26
+ "node": ">=16"
27
+ },
28
+ "scripts": {
29
+ "build": "pkgroll",
30
+ "lint": "biome lint --write src/*.ts",
31
+ "test": "npm run build && npm run lint && tsx test.ts"
32
+ },
33
+ "keywords": [
34
+ "errors",
35
+ "try",
36
+ "catch",
37
+ "error handling",
38
+ "nice-try",
39
+ "good-try",
40
+ "go-go-try",
41
+ "gotry",
42
+ "go-try"
43
+ ],
44
+ "devDependencies": {
45
+ "@biomejs/biome": "^1.9.4",
46
+ "pkgroll": "^2.12.1",
47
+ "tsx": "^4.19.3",
48
+ "typescript": "^5.8.3"
49
+ }
50
+ }
package/src/index.ts CHANGED
@@ -1,102 +1,90 @@
1
- import pIsPromise from 'p-is-promise'
2
- type ResultTuple<T> = readonly [undefined, T] | readonly [string, undefined]
3
- type RawResultTuple<T, E = unknown> =
4
- | readonly [undefined, T]
5
- | readonly [E, undefined]
1
+ export type Success<T> = readonly [undefined, T]
6
2
 
7
- type ErrorWithMessage = {
8
- message: string
3
+ export type Failure<E> = readonly [E, undefined]
4
+
5
+ export type Result<E, T> = Success<T> | Failure<E>
6
+
7
+ export function isSuccess<E, T>(result: Result<E, T>): result is Success<T> {
8
+ return result[0] === undefined
9
9
  }
10
10
 
11
- function isErrorWithMessage(error: unknown): error is ErrorWithMessage {
12
- return (
13
- typeof error === 'object' &&
14
- error !== null &&
15
- 'message' in error &&
16
- typeof (error as Record<string, unknown>).message === 'string'
17
- )
11
+ export function isFailure<E, T>(result: Result<E, T>): result is Failure<E> {
12
+ return result[0] !== undefined
18
13
  }
19
14
 
20
- function toErrorWithMessage(maybeError: unknown): ErrorWithMessage {
21
- if (isErrorWithMessage(maybeError)) {
22
- return maybeError
23
- }
15
+ export function success<T>(value: T): Success<T> {
16
+ return [undefined, value] as const
17
+ }
24
18
 
25
- try {
26
- return new Error(JSON.stringify(maybeError))
27
- } catch {
28
- // fallback in case there's an error stringifying the maybeError
29
- // with circular references for example.
30
- return new Error(String(maybeError))
31
- }
19
+ export function failure<E>(error: E): Failure<E> {
20
+ return [error, undefined] as const
32
21
  }
33
22
 
34
23
  function getErrorMessage(error: unknown): string {
35
- return toErrorWithMessage(error).message
36
- }
24
+ if (typeof error === 'string') return error
37
25
 
38
- function isPromise<T>(p: T | PromiseLike<T>): p is PromiseLike<T> {
39
- return pIsPromise(p)
40
- }
26
+ if (
27
+ typeof error === 'object' &&
28
+ error !== null &&
29
+ 'message' in error &&
30
+ typeof (error as Record<string, unknown>).message === 'string'
31
+ ) {
32
+ return (error as { message: string }).message
33
+ }
41
34
 
42
- function goTry<T>(value: PromiseLike<T>): PromiseLike<ResultTuple<T>>
43
- function goTry<T>(value: () => T): ResultTuple<T>
44
- function goTry<T>(
45
- value: (() => T) | PromiseLike<T>,
46
- ): ResultTuple<T> | PromiseLike<ResultTuple<T>> {
47
- let unwrappedValue: T | PromiseLike<T>
48
- try {
49
- unwrappedValue = typeof value === 'function' ? value() : value
50
- if (isPromise(unwrappedValue)) {
51
- return Promise.resolve(unwrappedValue)
52
- .then((value) => [undefined, value] as const)
53
- .catch((err) => [getErrorMessage(err), undefined] as const)
54
- }
55
- return [undefined, unwrappedValue] as const
56
- } catch (err) {
57
- return [getErrorMessage(err), undefined] as const
58
- }
35
+ try {
36
+ return JSON.stringify(error)
37
+ } catch {
38
+ return String(error)
39
+ }
59
40
  }
60
41
 
61
- function goTryRaw<T, E = unknown>(
62
- value: PromiseLike<T>,
63
- ): PromiseLike<RawResultTuple<T, E>>
64
- function goTryRaw<T, E = unknown>(value: () => T): RawResultTuple<T, E>
65
- function goTryRaw<T, E = unknown>(
66
- value: PromiseLike<T> | (() => T),
67
- ): RawResultTuple<T, E> | PromiseLike<RawResultTuple<T, E>> {
68
- let unwrappedValue: T | PromiseLike<T>
69
- try {
70
- unwrappedValue = typeof value === 'function' ? value() : value
71
- if (isPromise(unwrappedValue)) {
72
- return Promise.resolve(unwrappedValue)
73
- .then((value) => [undefined, value] as const)
74
- .catch((err) => [err, undefined] as const)
75
- }
76
- return [undefined, unwrappedValue] as const
77
- } catch (err) {
78
- return [err, undefined] as const as RawResultTuple<T, E>
79
- }
42
+ function isPromiseLike<T>(value: unknown): value is PromiseLike<T> {
43
+ return (
44
+ typeof value === 'object' &&
45
+ value !== null &&
46
+ 'then' in value &&
47
+ typeof (value as { then: unknown }).then === 'function'
48
+ )
80
49
  }
50
+ export function goTry<T>(fn: () => T): Result<string, T>
51
+ export function goTry<T>(value: T): Result<string, T>
52
+ export function goTry<T>(promise: PromiseLike<T>): Promise<Result<string, T>>
53
+
54
+ export function goTry<T>(
55
+ value: T | (() => T) | PromiseLike<T>,
56
+ ): Result<string, T> | Promise<Result<string, T>> {
57
+ if (isPromiseLike<T>(value)) {
58
+ return Promise.resolve(value)
59
+ .then((value) => success(value))
60
+ .catch((err) => failure(getErrorMessage(err)))
61
+ }
81
62
 
82
- async function goExpect<T>(
83
- value: (() => T) | PromiseLike<T>,
84
- error?: (err: string) => string,
85
- ): Promise<T> {
86
- const _error = error ?? ((e: string): string => e)
87
- try {
88
- const unwrappedValue = typeof value === 'function' ? value() : value
89
- if (isPromise(unwrappedValue)) {
90
- const [err, res] = await goTry(unwrappedValue)
91
- if (err !== undefined) {
92
- throw new Error(err)
93
- }
94
- return res
95
- }
96
- return unwrappedValue
97
- } catch (err) {
98
- throw new Error(_error(getErrorMessage(err)))
99
- }
63
+ try {
64
+ const result = typeof value === 'function' ? (value as () => T)() : value
65
+ return success(result)
66
+ } catch (err) {
67
+ return failure(getErrorMessage(err))
68
+ }
100
69
  }
70
+ export function goTryRaw<E = unknown, T = unknown>(fn: () => T): Result<E, T>
71
+ export function goTryRaw<E = unknown, T = unknown>(value: T): Result<E, T>
72
+ export function goTryRaw<E = unknown, T = unknown>(
73
+ promise: PromiseLike<T>,
74
+ ): Promise<Result<E, T>>
101
75
 
102
- export { goTry, goExpect, goTryRaw }
76
+ export function goTryRaw<E = unknown, T = unknown>(
77
+ value: T | (() => T) | PromiseLike<T>,
78
+ ): Result<E, T> | Promise<Result<E, T>> {
79
+ if (isPromiseLike<T>(value)) {
80
+ return Promise.resolve(value)
81
+ .then((value) => success<T>(value))
82
+ .catch((err) => failure<E>(err as E))
83
+ }
84
+ try {
85
+ const result = typeof value === 'function' ? (value as () => T)() : value
86
+ return success<T>(result)
87
+ } catch (err) {
88
+ return failure<E>(err as E)
89
+ }
90
+ }
package/test.ts CHANGED
@@ -1,87 +1,119 @@
1
- import { goExpect, goTry, goTryRaw } from './src/index'
2
-
3
- describe('go-go-try', () => {
4
- it(`value returned by callback is used when callback doesn't throw`, async () => {
5
- const fn = () => 'value'
6
- const [err1, value] = goTry(fn)
7
- let value2
8
- try {
9
- value2 = await goExpect(fn)
10
- } catch (err) {
11
- // this won't throw
12
- }
13
- const [err2, value3] = goTryRaw(fn)
14
- expect(value).toBe('value')
15
- expect(value2).toBe('value')
16
- expect(value3).toBe('value')
17
- expect(err1).toBeUndefined()
18
- expect(err2).toBeUndefined()
19
- })
20
-
21
- it('if callback throws, value should be undefined and err should contain the error message', async () => {
22
- const fn = () => {
23
- return JSON.parse('{/')
24
- }
25
- const [err1, value] = goTry(fn)
26
- let value2
27
- let err2
28
- try {
29
- value2 = await goExpect(fn)
30
- } catch (err) {
31
- if (err instanceof Error) {
32
- err2 = err.message
33
- }
34
- }
35
- const [err3, value3] = goTryRaw(fn)
36
-
37
- expect(value).toBeUndefined()
38
- expect(value2).toBeUndefined()
39
- expect(value3).toBeUndefined()
40
- expect(typeof err1).toBe('string')
41
- expect(typeof err2).toBe('string')
42
- expect(typeof err3).toBe('object')
43
- expect(typeof (err3 as { message: string }).message).toBe('string')
44
- })
45
-
46
- it('first parameter accepts promises and makes the function async', async () => {
47
- const fn = Promise.resolve('value')
48
- const [err1, value] = await goTry(fn)
49
- let value2
50
- let err2
51
- try {
52
- value2 = await goExpect(fn)
53
- } catch (err) {
54
- err2 = err
55
- }
56
- const [err3, value3] = await goTryRaw(fn)
57
- expect(value).toBe('value')
58
- expect(value2).toBe('value')
59
- expect(value3).toBe('value')
60
- expect(err1).toBeUndefined()
61
- expect(err2).toBeUndefined()
62
- expect(err3).toBeUndefined()
63
- })
64
-
65
- it('if async callback throws, value should be undefined and err should contain the error message', async () => {
66
- const fn = Promise.reject(new Error('error'))
67
- const [err, value] = await goTry(fn)
68
- let value2
69
- let err2
70
- try {
71
- value2 = await goExpect(fn, (e) => `custom ${e}`)
72
- } catch (err) {
73
- if (err instanceof Error) {
74
- err2 = err.message
75
- console.log(err)
76
- }
77
- }
78
- const [err3, value3] = await goTryRaw(fn)
79
- expect(value).toBeUndefined()
80
- expect(value2).toBeUndefined()
81
- expect(value3).toBeUndefined()
82
- expect(err).toBe('error')
83
- expect(err2).toBe('custom error')
84
- expect(typeof err3).toBe('object')
85
- expect((err3 as { message: string }).message).toEqual('error')
86
- })
1
+ import * as assert from 'node:assert'
2
+ import { test } from 'node:test'
3
+ import { goTry, goTryRaw, isFailure, isSuccess } from './src/index.js'
4
+
5
+ test(`value returned by callback is used when callback doesn't throw`, async () => {
6
+ const fn = () => 'value'
7
+
8
+ // Test with function
9
+ const result1 = goTry(fn)
10
+ const result2 = goTryRaw(fn)
11
+
12
+ // Test destructuring
13
+ const [err1, value] = result1
14
+ const [err2, value2] = result2
15
+
16
+ // Test type guards
17
+ assert.equal(isSuccess(result1), true)
18
+ assert.equal(isSuccess(result2), true)
19
+ assert.equal(isFailure(result1), false)
20
+ assert.equal(isFailure(result2), false)
21
+
22
+ // Test values
23
+ assert.equal(value, 'value')
24
+ assert.equal(value2, 'value')
25
+ assert.equal(err1, undefined)
26
+ assert.equal(err2, undefined)
27
+ })
28
+
29
+ test('if callback throws, value should be undefined and err should contain the error message', async () => {
30
+ const fn = () => {
31
+ return JSON.parse('{/') as string
32
+ }
33
+
34
+ // Test with function that throws
35
+ const result1 = goTry(fn)
36
+ const result2 = goTryRaw<Error>(fn)
37
+
38
+ // Test destructuring
39
+ const [err1, value] = result1
40
+ const [err2, value2] = result2
41
+
42
+ // Test type guards
43
+ assert.equal(isSuccess(result1), false)
44
+ assert.equal(isSuccess(result2), false)
45
+ assert.equal(isFailure(result1), true)
46
+ assert.equal(isFailure(result2), true)
47
+
48
+ // Test values
49
+ assert.equal(value, undefined)
50
+ assert.equal(value2, undefined)
51
+ assert.equal(typeof err1, 'string')
52
+ assert.equal(typeof err2, 'object')
53
+ assert.equal(typeof err2?.message, 'string')
54
+ })
55
+
56
+ test('first parameter accepts promises and makes the function async', async () => {
57
+ const promise = Promise.resolve('value')
58
+
59
+ // Test with promise
60
+ const result1 = await goTry(promise)
61
+ const result2 = await goTryRaw(promise)
62
+
63
+ // Test destructuring
64
+ const [err1, value] = result1
65
+ const [err2, value2] = result2
66
+
67
+ // Test type guards
68
+ assert.equal(isSuccess(result1), true)
69
+ assert.equal(isSuccess(result2), true)
70
+ assert.equal(isFailure(result1), false)
71
+ assert.equal(isFailure(result2), false)
72
+
73
+ // Test values
74
+ assert.equal(value, 'value')
75
+ assert.equal(value2, 'value')
76
+ assert.equal(err1, undefined)
77
+ assert.equal(err2, undefined)
78
+ })
79
+
80
+ test('if async callback throws, value should be undefined and err should contain the error message', async () => {
81
+ const promise = Promise.reject(new Error('error'))
82
+
83
+ // Test with promise that rejects
84
+ const result1 = await goTry(promise)
85
+ const result2 = await goTryRaw<Error>(promise)
86
+
87
+ // Test destructuring
88
+ const [err, value] = result1
89
+ const [err2, value2] = result2
90
+
91
+ // Test type guards
92
+ assert.equal(isSuccess(result1), false)
93
+ assert.equal(isSuccess(result2), false)
94
+ assert.equal(isFailure(result1), true)
95
+ assert.equal(isFailure(result2), true)
96
+
97
+ // Test values
98
+ assert.equal(value, undefined)
99
+ assert.equal(value2, undefined)
100
+ assert.equal(err, 'error')
101
+ assert.equal(typeof err2, 'object')
102
+ assert.equal(err2?.message, 'error')
103
+ })
104
+
105
+ test('direct values work too', () => {
106
+ // Test with direct value
107
+ const result1 = goTry('value')
108
+ const result2 = goTryRaw('value')
109
+
110
+ // Test destructuring
111
+ const [err1, value] = result1
112
+ const [err2, value2] = result2
113
+
114
+ // Test values
115
+ assert.equal(value, 'value')
116
+ assert.equal(value2, 'value')
117
+ assert.equal(err1, undefined)
118
+ assert.equal(err2, undefined)
87
119
  })
package/tsconfig.json CHANGED
@@ -1,24 +1,11 @@
1
1
  {
2
2
  "compilerOptions": {
3
3
  "target": "ES2020",
4
- // previously "CommonJS", we now use "ES2015" to transpile TypeScript to "export default"
5
- // instead of "exports.default ="
6
4
  "module": "ES2015",
7
5
  "esModuleInterop": true,
8
- // From what I understand while reading the docs "classic" was introduced first and "classic" is by
9
- // default(except when "module": "commonjs") because of backwards compatibility.
10
- //
11
- // From the docs: "node module resolution is the most-commonly used in the TypeScript community and is
12
- // recommended for most projects."
13
6
  "moduleResolution": "node",
14
- // the npm package include ".js" files only so we need ".d.ts" files to be generated
15
7
  "declaration": true,
16
- // - ℹ️ https://www.typescriptlang.org/tsconfig#isolatedModules
17
- // - 😃 Deno requires "isolatedModules" to be set to `true`
18
- // - 😃 the stricter version - I always prefer the stricter version
19
- // - 😃 probably the future
20
8
  "isolatedModules": true,
21
- // strict options ensuring more stable code
22
9
  "strict": true,
23
10
  "noUnusedLocals": true,
24
11
  "noUnusedParameters": true,
@@ -26,8 +13,5 @@
26
13
  "noUncheckedIndexedAccess": true,
27
14
  "noFallthroughCasesInSwitch": true,
28
15
  "forceConsistentCasingInFileNames": true
29
- },
30
- "include": [
31
- "src/index.ts"
32
- ]
16
+ }
33
17
  }
package/jest.config.ts DELETED
@@ -1,36 +0,0 @@
1
- import type { Config } from '@jest/types'
2
-
3
- const config: Config.InitialOptions = {
4
- // adds support for tests written in TypeScript
5
- preset: 'ts-jest',
6
-
7
- // adds jsdom (https://github.com/jsdom/jsdom) APIs to tests
8
- testEnvironment: 'jsdom',
9
-
10
- // run tests on root and in /test folder with .ts and .tsx extensions
11
- testMatch: ['<rootDir>/test.ts?(x)', '<rootDir>/test/**.ts?(x)'],
12
-
13
- // ESM support
14
- // https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/
15
- globals: {
16
- 'ts-jest': {
17
- useESM: true,
18
- },
19
- },
20
- extensionsToTreatAsEsm: ['.ts', '.tsx'],
21
-
22
- // restores original implementation (after each test is finished) when calling `jest.spyOn()`
23
- // inside of a test:
24
- // - https://jestjs.io/docs/configuration#restoremocks-boolean
25
- // - https://jestjs.io/docs/mock-function-api#mockfnmockrestore
26
- restoreMocks: true,
27
-
28
- // 🐛 fix "having test.js and test.ts runs the test.js file"
29
- // what about: ts-node CLI option:
30
- // --prefer-ts-exts Re-order file extensions so that TypeScript imports are preferred (TS_NODE_PREFER_TS_EXTS, default: false)
31
- moduleNameMapper: {
32
- '^(\\..*)$': ['$1/index.ts', '$1.ts', '$1'],
33
- },
34
- }
35
-
36
- export default config