Skip to content

Suggestion: throws clause and typed catch clause #13219

Closed as not planned
microsoft/workspace-tools
#86
@nitzantomer

Description

The typescript type system is helpful in most cases, but it can’t be utilized when handling exceptions.
For example:

function fn(num: number): void {
    if (num === 0) {
        throw "error: can't deal with 0";
    }
}

The problem here is two fold (without looking through the code):

  1. When using this function there’s no way to know that it might throw an error
  2. It’s not clear what the type(s) of the error is going to be

In many scenarios these aren't really a problem, but knowing whether a function/method might throw an exception can be very useful in different scenarios, especially when using different libraries.

By introducing (optional) checked exception the type system can be utilized for exception handling.
I know that checked exceptions isn't agreed upon (for example Anders Hejlsberg), but by making it optional (and maybe inferred? more later) then it just adds the opportunity to add more information about the code which can help developers, tools and documentation.
It will also allow a better usage of meaningful custom errors for large big projects.

As all javascript runtime errors are of type Error (or extending types such as TypeError) the actual type for a function will always be type | Error.

The grammar is straightforward, a function definition can end with a throws clause followed by a type:

function fn() throws string { ... }
function fn(...) throws string | number { ... }

class MyError extends Error { ... }
function fn(...): Promise<string> throws MyError { ... }

When catching the exceptions the syntax is the same with the ability to declare the type(s) of the error:
catch(e: string | Error) { ... }

Examples:

function fn(num: number): void throws string {
    if (num === 0) {
        throw "error: can't deal with 0";
    }
}

Here it’s clear that the function can throw an error and that the error will be a string, and so when calling this method the developer (and the compiler/IDE) is aware of it and can handle it better.
So:

fn(0);

// or
try {
    fn(0); 
} catch (e: string) { ... }

Compiles with no errors, but:

try {
    fn(0); 
} catch (e: number) { ... }

Fails to compile because number isn't string.

Control flow and error type inference

try {
    fn(0);
} catch(e) {
    if (typeof e === "string") {
        console.log(e.length);
    } else if (e instanceof Error) {
        console.log(e.message);
    } else if (typeof e === "string") {
        console.log(e * 3); // error: Unreachable code detected
    }

    console.log(e * 3); // error: The left-hand side of an arithmetic operation must be of type 'any', 'number' or an enum type
}
function fn(num: number): void {
    if (num === 0) {
        throw "error: can't deal with 0";
    }
}

Throws string.

function fn2(num: number) {
    if (num < 0) {
        throw new MyError("can only deal with positives");
    }

    fn(num);
}

Throws MyError | string.
However:

function fn2(num: number) {
    if (num < 0) {
        throw new MyError("can only deal with positives");
    }

    try {
        fn(num);
    } catch(e) {
        if (typeof e === "string") {
           throw new MyError(e);
       } 
    }
}

Throws only MyError.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    DeclinedThe issue was declined as something which matches the TypeScript visionSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions