Skip to content

Restrictive type on callbacks that are guaranteed to error #1613

@Nemo157

Description

@Nemo157

Motivation

It's sometimes very useful to be able to define callbacks that guarantee an error. For example; transforming errors in when.js promises to hide implementation details:

function stuff(): when.Promise<Item> {
    return when.reject<Item>(new Error('Oh noes!'));
}

export function tryStuff(): when.Promise<Item> {
    return stuff().catch(err => {
        log.error('Internal error', err);
        throw new InternalError();
    });
}

Unfortunately the compiler derives the type of the error handler to be (err: any) => void and propagates the return type through the definition of catch resulting in

error TS2012: Cannot convert 'when.Promise<void>' to 'when.Promise<Item>'

Even if the definition of tryStuff is changed to force the result to the correct type

return stuff().catch<Item>(err => {
    log.error('Internal error', err);
    throw new InternalError();
});

the same type is derived for the error handler and you instead get

error TS2082: Supplied parameters do not match any signature of call target:
        Call signatures of types '(err: any) => void' and '(reason: any) => when.Promise<Item>' are incompatible.

Details

A fully specified example of the same behaviour is

function run<T>(callback: () => T): T {
    return callback();
}

run<boolean>(() => {
    throw new Error('Failure');
});

which gives the error

error TS2082: Supplied parameters do not match any signature of call target:
        Call signatures of types '() => void' and '() => boolean' are incompatible.

Two possible solutions are returning null to trick the compiler into giving the callback a lenient type (I guess it defaults to any when the only return returns a null), or explicitly telling the compiler that the callback can return anything.

run<boolean>(() => {
    throw new Error('Failure');
    return null;
});

run<boolean>(<() => any>(() => {
    throw new Error('Failure');
}));

The first runs afoul of unreachable statement lints. The second is just really horrible.

Seeing as the lints are capable of determining that there is no way to return from this callback, it would be nice if the compiler could also check this and give the callback the type () => any when it can guarantee there is no possibility of returning.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions