Proper Exceptions

Topics: General, Language Specification
Nov 4, 2013 at 10:28 PM
Based on this answer on StackOverflow I took a stab at writing something closer to a "real" working Exception-class for TypeScript; something I desperately wish the language itself provided.

My goal is to have a class I can actually extend, unlike Error which I can only implement - you don't get proper error-reporting (line-numbers and stack-trace) that way, and you can't do proper type-checking in catch blocks using instanceof...

Here's what I got so far:
window['Exception'] = (function() {
    var Exception, err;
    Exception = (function() {
        function Exception(message) {
            var err;
            err = new Error(message);
            err.name = "Exception";
            this.message = err.message;
            if (err.stack) {
                this.stack = err.stack;
            }
        }
        return Exception;
    })();
    err = new Error();
    err.name = "Exception";
    Exception.prototype = err;
    
    return Exception;
})();

declare class Exception implements Error {
    name: string;
    message: string;
    
    constructor(message?: string);
}
With this, you get a new base-type called Exception that you can extend:
class NotImplemented extends Exception {
    constructor(message?: string) {
        super(message || 'Not Implemented');
    }
}
Custom exception-classes will pass various type-checks:
console.log(new Exception() instanceof Error); // => true
console.log(new Exception() instanceof Exception); // => true
console.log(new NotImplemented() instanceof Error); // => true
console.log(new NotImplemented() instanceof Exception); // => true
console.log(new NotImplemented() instanceof NotImplemented); // => true
It has the message and name properties:
console.log("message: " + (new NotImplemented("Hello World").message));
console.log("name: " + (new NotImplemented().name));
And you can throw it:
throw new NotImplemented();
Honestly, I'm just sort of stabbing in the dark here - I don't fully understand how this works, and maybe it doesn't work, or doesn't do what I think it does...

I would really like to hear from other "JavaScript gurus" out there.

If this can be done (?) with "standard" JavaScript code, I would really like to have support for this in TypeScript, somehow... existing Exception handling seems really feeble.

PS: anyone know why throw/try/catch isn't even mentioned in the TypeScript language spec? Is it not officially part of the language?? Or just not documented yet?
Developer
Nov 5, 2013 at 8:57 PM
PS: anyone know why throw/try/catch isn't even mentioned in the TypeScript language spec? Is it not officially part of the language?? Or just not documented yet?
The latter. You'll see them documented in an upcoming version of the spec.
Nov 5, 2013 at 9:27 PM
Edited Nov 5, 2013 at 9:27 PM
danquirk wrote:
The latter. You'll see them documented in an upcoming version of the spec.
Just the statements? Or has the team made any progress on making Error more extensible or useful than what plain JavaScript has?

I strongly dislike throwing plain Error objects, but I'm starting to think it may be the best option right now.

For the time being, I'm leaning towards the least complex solution, e.g. adding an error-type property (with an enum) to a new Error() before I throw it...
Jul 7, 2014 at 2:14 PM
Any progress on this issue?

The spec has like two paragraphs on try/catch at this point, and not a word about errors/exceptions... this stuff is important, isn't it?
Developer
Jul 7, 2014 at 9:11 PM
Currently TypeScript supports what JavaScript does as you've noted. We haven't heard significant amounts of feedback on this issue. Thus far TypeScript has avoided added any 'core library' of its own with functionality like this.
Jul 9, 2014 at 12:28 PM
Edited Jul 9, 2014 at 12:30 PM
The problem is the lack of ability to extend the JavaScript Error class - this can only be done using non-type-safe hacks.

For now, I'm using the following pattern: I have a dedicated app.error module with chainable functions to configure Error instances, e.g.:
    export function undefinedName(error: Error, name: string): Error {
        error.message = 'Undefined name: ' + name;
        return error;
    }
Since backtraces only work correctly when Error instances are constructed at the site of the exception, I use them like this:
throw app.error.undefinedName(new Error(), 'Rasmus');
It "works" as far as reporting the site of the error and the correct error message - but there is no way to, for example, check the type of an exception, since the only possible type is Error... We can work around this by adding a type-property in a non-type-safe way, and then checking for that property in a non-type-safe way after trapping an exception, but... I keep wishing these patterns/hacks/workarounds could be encapsulated somehow, so you could actually just work with exceptions naturally, the way you'd expect from other languages...
Jul 10, 2014 at 12:18 PM
As far as I can see, it's not a language issue at all, and the topic starter will be fine with just some simple changes to lib.d.ts, namely replacing "interface Error" with "declare class Error" and corresponding changes to Error subclasses.