Hello everyone.
Have you ever felt that Try/Catch is a bit inconvenient when developing an application in TypeScript?
I luckily found an amazing video on YouTube that describes how to handle errors in TypeScript in a simple way.
I'm sharing insights from the video as a review.
Defining the getUser function for error handling
First of all, I defined a simple getUser function to illustrate error handling.
It returns a new user with the given id.
const wait = (duration: number) => {
return new Promise((resolve) => {
setTimeout(resolve, duration);
});
};
const getUser = async (id: number) => {
await wait(1000);
if (id === 2) {
throw new Error("404 - User does not exist");
}
return { id, name: "Noah" };
};
const user = await getUser(1);
console.log(user); // { id: 1, name: "Noah" }
Error Handling using try/catch
Rewriting the previous code using try/catch, it looks like this.
const wait = (duration: number) => {
...
};
const getUser = async (id: number) => {
...
};
try {
const user = await getUser(1);
console.log(user); // { id: 1, name: "Noah" }
} catch (error) {
console.log("There was an error");
}
Problem with try/catch ①: It handles every error that occurs within the try block
The code below is not ideal.
Even though it's just a typo, "There was an error" is displayed in the console. I only want to handle errors that occur specifically in getUser within this try/catch block.
const wait = (duration: number) => {
...
};
const getUser = async (id: number) => {
...
};
try {
const user = await getUser(1);
console.log(usr); // ← There was an error
// ... (a lot of code)
} catch (error) {
console.log("There was an error");
}
Problem with try/catch ②: The Pitfall of Using let
Okay then, let's try to solve it using let
.
const wait = (duration: number) => {
...
};
const getUser = async (id: number) => {
...
};
let user;
try {
user = await getUser(1);
// ... (a lot of code)
} catch (error) {
console.log("There was an error");
}
console.log(usr); // ← ReferenceError: Can't find variable: usr
I got an actual error from the typo, but this code is still not ideal because I can accidentally redefine the user object, like below.
const wait = (duration: number) => {
...
};
const getUser = async (id: number) => {
...
};
let user;
try {
user = await getUser(1);
// ... (a lot of code)
} catch (error) {
console.log("There was an error");
}
user = 1 // ← ❌ It might lead to a bug.
Ideal solution
It's much simpler and more readable, don't you think?
Furthermore, the user variable is immutable and won't lead to unexpected errors.
const wait = (duration: number) => {
...
};
const getUser = async (id: number) => {
...
};
const catchError = async <T>(promise: Promise<T>): Promise<[undefined, T] | [Error]> => {
return promise
.then((data) => {
return [undefined, data] as [undefined, T];
})
.catch((error) => {
return [error];
});
};
const [error, user] = await catchError(getUser(1));
if (error) {
console.log(error);
}
console.log(user);
Conclusion
By using this function for error handling, we can significantly simplify our code and make it cleaner and easier to maintain. Instead of scattering multiple "try/catch" blocks throughout the code, this approach keeps our error handling consistent and less noisy. It’s a smarter, more efficient way to manage errors in TypeScript.
Please take a look at the video, which we have referenced. He explains it very carefully.
Happy Coding☀️
Top comments (7)
This is a great breakdown of the limitations of try/catch and a more elegant solution for handling errors in TypeScript. The
catchError
function looks incredibly useful for maintaining clean and readable code.@programmerraja
Thank you for the kind words! I’m glad you found the breakdown helpful. Yes, the
catchError
function is indeed a handy way to manage errors elegantly in TypeScript. It really makes a difference in keeping the codebase maintainable and easier to understand. If you have any thoughts or suggestions on improving the approach, I’d love to hear them!Inspired by golang error handling 😀👍
@dhananjayve
I saw that mentioned in his YouTube comments as well. I haven’t used Golang before, but I’ll look into it. Thank you!
I like the syntax that it creates and the inspiration from Golang and the
?=
syntax that has recently been proposed and is in very early stage.I do wonder about how much benefit this has since you now have to have 2 conditions where the error is handled, one in the catch block and one in the if block.
If you are wanting to suppress the error but still add a log of some sort, you can simply use a combination of await / catch
This is less code and allows you to avoid using let
I don't quite see the advantage of this over using
const user = await getUser(1).catch(console.log);
In what cases is your suggestion better?
The advantage is more clear when you need to do more than console log the error. If you need to make a decision based on the error value, this pattern removes duplicated logic and enforces a consistent pattern in your code