Error Handling
Error handling in both languages is mostly congruent (as opposed to Go, for example). Both follow the try-catch-finally
exception handling construct.
Error handling in C# is centered around exceptions, which are objects representing runtime errors. Unlike JavaScript and TypeScript, where errors can be any value (including simple strings), C# enforces structured exception handling using the try
, catch
, finally
, and throw
keywords. Exceptions in C# derive from System.Exception
, allowing for a robust type hierarchy where specific exceptions (e.g., NullReferenceException
, ArgumentException
) provide more context about failures.
In contrast, JavaScript and TypeScript use a more flexible error-handling approach. While they support try...catch...finally
, errors are not required to follow a strict class-based structure—any value, even a string, can be thrown. TypeScript improves error handling slightly by enabling type annotations, but it does not enforce exception types like C#. Additionally, unhandled promise rejections in JavaScript (from asynchronous operations) can silently fail if not explicitly caught, whereas C#’s Task
and async
methods throw exceptions that must be handled explicitly, reducing the risk of silent failures.
Throwing Exceptions
throw new Error("Oops!");
throw new Exception("Oops!");
Try-Catch-Finally
try {
// Work here
} catch {
// Handle error here
}
try {
// Work here
} catch (err) {
// Handle error here
} finally {
// Always executed
}
try {
// Work here
} catch {
// Handle error here
}
try {
// Work here
} catch (Exception ex) {
// Handle error here
} finally {
// Always executed
}
Exception Types
class NotFoundError extends Error {
constructor(message) {
super(message)
}
}
class NotFoundException : Exception {
public NotFoundException(string message)
: base(message) { }
}
// Using a primary constructor (see later docs)
class NotFoundException(
string message
) : Exception(message) { }
Now we can filter on the type of exception. The mechanism is cleaner in C#.
try {
// Work here
} catch (err) {
if (err instanceof NotFoundError) {
// Handle NotFoundError
} else {
// Handle all other errors
}
} finally {
// Always executed
}
try {
// Work here
} catch (NotFoundException) {
// Handle NotFoundException
} catch (Exception) {
// Handle all generic exceptions
} finally {
// Always executed
}
Best Practices
Rethrowing
try {
// Work here
} catch (err) {
// Handle then rethrow
throw err;
} finally {
// Always executed
}
try {
// Work here
} catch (Exception) {
// 👇 NOTE that this DOES NOT use `throw ex;`
throw;
} finally {
// Always executed
}
WARNING
In C#, catch(Exception ex) { throw; }
is not the same as catch(Exception ex) { throw ex; }
. In the former, the stack trace is maintained. In the latter, the stack trace will be reset. Prefer the former versus the latter!