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 (1)
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.