@shkumbinhsn/try-catch 0.0.1
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/README.md +100 -0
- package/lib/try-catch.d.ts +26 -0
- package/lib/try-catch.js +29 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Type-Safe Try-Catch Pattern for TypeScript
|
|
2
|
+
|
|
3
|
+
This repository demonstrates an experimental approach to handling try-catch blocks in TypeScript with enhanced type safety and flexibility. This pattern allows developers to explicitly define the types of errors that can be thrown while maintaining compatibility with traditional try-catch usage.
|
|
4
|
+
|
|
5
|
+
## Key Features
|
|
6
|
+
|
|
7
|
+
1. **Full TypeScript Flexibility**:
|
|
8
|
+
- Use the `Throws` utility to define the types of errors a function might throw.
|
|
9
|
+
- If type definitions are not needed, the function works like a traditional try-catch and returns a tuple `[data, error]`.
|
|
10
|
+
|
|
11
|
+
2. **Explicit Error Types**:
|
|
12
|
+
- For functions throwing multiple errors (e.g., `CustomError`, `Error`), use `Throws` to explicitly declare them.
|
|
13
|
+
|
|
14
|
+
3. **Ease of Use**:
|
|
15
|
+
- Handle returned tuples with an `if` statement to separate success from failure cases.
|
|
16
|
+
|
|
17
|
+
## Limitations
|
|
18
|
+
|
|
19
|
+
- **Duplicate Definitions**:
|
|
20
|
+
- Errors need to be defined twice: once for throwing them and once in the return type. This is a design choice to ensure type safety, but it does not affect runtime behavior.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Example Code
|
|
25
|
+
|
|
26
|
+
### Defining a Function with Error Handling
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { tryCatch, type Throws } from "./lib/try-catch";
|
|
30
|
+
|
|
31
|
+
class CustomError extends Error {}
|
|
32
|
+
|
|
33
|
+
function iMightFail(): string & Throws<CustomError> {
|
|
34
|
+
const random = Math.random();
|
|
35
|
+
if (random > 0.2) {
|
|
36
|
+
return "success";
|
|
37
|
+
} else if (random > 0.5) {
|
|
38
|
+
throw new CustomError();
|
|
39
|
+
}
|
|
40
|
+
throw new Error();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const [data, error] = tryCatch(() => iMightFail());
|
|
44
|
+
|
|
45
|
+
if (error1) {
|
|
46
|
+
console.log("i Might fail failed", error.message);
|
|
47
|
+
// ^? Error | CustomError
|
|
48
|
+
} else {
|
|
49
|
+
console.log("i succeeded", data);
|
|
50
|
+
// ^? string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Async Functions with Errors
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
function sleep(ms: number) {
|
|
59
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function iMightFailAsync(): Promise<string & Throws<CustomError>> {
|
|
63
|
+
await sleep(200);
|
|
64
|
+
const random = Math.random();
|
|
65
|
+
if (random > 0.2) {
|
|
66
|
+
return "success";
|
|
67
|
+
} else if (random > 0.5) {
|
|
68
|
+
throw new CustomError();
|
|
69
|
+
}
|
|
70
|
+
throw new Error();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [data, error] = await tryCatch(() => iMightFailAsync());
|
|
74
|
+
|
|
75
|
+
if (error2) {
|
|
76
|
+
console.log("i Might fail async failed", error.message);
|
|
77
|
+
// ^? Error | CustomError
|
|
78
|
+
} else {
|
|
79
|
+
console.log("i Might fail async succeeded", data);
|
|
80
|
+
// ^? string
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### With normal typescript inference
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
function iMightFailOrNot() {
|
|
88
|
+
return "success"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const [data3, error3] = tryCatch(iMightFailOrNot);
|
|
92
|
+
|
|
93
|
+
if (error3) {
|
|
94
|
+
console.log("i Might fail or not failed", error3.message)
|
|
95
|
+
// ^? Error
|
|
96
|
+
} else {
|
|
97
|
+
console.log("i Might fail or not succeeded", data3)
|
|
98
|
+
// ^? string
|
|
99
|
+
}
|
|
100
|
+
```
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const errorSymbol = Symbol();
|
|
2
|
+
type ErrorSymbol = typeof errorSymbol;
|
|
3
|
+
|
|
4
|
+
export type Throws<T extends Error> = {
|
|
5
|
+
[errorSymbol]?: T
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type ExtractErrors<T> = T extends Throws<infer E> ? E : never;
|
|
9
|
+
|
|
10
|
+
type Simplify<T> = T extends any ? { [K in keyof T]: T[K] } : never;
|
|
11
|
+
|
|
12
|
+
type ExtractData<T> =
|
|
13
|
+
T extends string ? string :
|
|
14
|
+
T extends number ? number :
|
|
15
|
+
T extends boolean ? boolean :
|
|
16
|
+
T extends symbol ? symbol :
|
|
17
|
+
T extends bigint ? bigint :
|
|
18
|
+
T extends null ? null :
|
|
19
|
+
T extends undefined ? undefined :
|
|
20
|
+
T extends Function ? T :
|
|
21
|
+
Simplify<Omit<T, ErrorSymbol>>;
|
|
22
|
+
|
|
23
|
+
type DataError<T> = [ExtractData<T>, null] | [null, Error | ExtractErrors<T>];
|
|
24
|
+
type TryCatchReturn<T> = T extends Promise<infer R> ? Promise<DataError<R>> : DataError<T>;
|
|
25
|
+
|
|
26
|
+
export declare function tryCatch<T>(fn: () => T): TryCatchReturn<T>;
|
package/lib/try-catch.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executes a function within a try-catch block and returns a tuple [data, error].
|
|
3
|
+
*
|
|
4
|
+
* @param {Function} fn - The function to execute. Can be synchronous or return a Promise.
|
|
5
|
+
* @returns {Array|Promise<Array>} A tuple where:
|
|
6
|
+
* - For success: [data, null] where data is the function's return value
|
|
7
|
+
* - For failure: [null, error] where error is the caught exception
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // Synchronous usage
|
|
11
|
+
* const [data, error] = tryCatch(() => riskyFunction());
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Asynchronous usage
|
|
15
|
+
* const [data, error] = await tryCatch(() => asyncRiskyFunction());
|
|
16
|
+
*/
|
|
17
|
+
export function tryCatch(fn) {
|
|
18
|
+
try {
|
|
19
|
+
const result = fn();
|
|
20
|
+
if(result instanceof Promise) {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
result.then((data) => resolve([data, null])).catch((error) => resolve([null, error]))
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
return [result, null];
|
|
26
|
+
} catch (e) {
|
|
27
|
+
return [null, e]
|
|
28
|
+
}
|
|
29
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@shkumbinhsn/try-catch",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A utility package for handling try-catch blocks in TypeScript.",
|
|
5
|
+
"main": "./lib/try-catch.js",
|
|
6
|
+
"module": "./lib/try-catch.js",
|
|
7
|
+
"types": "./lib/try-catch.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/try-catch.d.ts",
|
|
11
|
+
"import": "./lib/try-catch.js",
|
|
12
|
+
"require": "./lib/try-catch.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=16"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"test": "echo \"No test specified\" && exit 0"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/shkumbinhasani/ts-try-catch"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"typescript",
|
|
33
|
+
"try-catch",
|
|
34
|
+
"error-handling",
|
|
35
|
+
"type-safe",
|
|
36
|
+
"utility",
|
|
37
|
+
"exception-handling",
|
|
38
|
+
"functional-programming",
|
|
39
|
+
"result-type"
|
|
40
|
+
],
|
|
41
|
+
"author": "Shkumbin Hasani",
|
|
42
|
+
"license": "MIT",
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"typescript": "^5.0.0"
|
|
45
|
+
}
|
|
46
|
+
}
|