Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

And if you hate the indentation from the `if (result) {}` you can combine this with the poor man's ? operator.

    const result = await funcThatReturnSomeType().catch(convertError); // result: SomeType | Error
    if (isError(result)) return result;
    // now result: SomeType
EDIT: the ? operator in question - https://doc.rust-lang.org/edition-guide/rust-2018/error-hand...


You can also get rid of `if(result){}` by setting the return type of "doSomethingWithErr" to "never":

    function doSomethingWithErr(err: any): never {
        throw new Error("Oops");
    }

    let result: SomeType;
    try {
        result = await funcThatReturnSomeType();
    } catch (err) {
        doSomethingWithErr(err);
    }
    // because doSomethingWithErr has return type "never", result will be definitely assigned.
    doSomething(result);

..or just return in the catch block.


Interestingly when I was encountering this myself recently, I discovered that JS finally blocks can return after a function has nominally already returned. Consider the following closure.

    (() => {
      try {
        // finally will return prior to this console.log
        console.log('this try was executed and the return ignored')
        return 'try block'
      } catch (e) {
        return 'error block'
      } finally {
        return 'finally block'
      }  
    })()


I don't think that's the right way of thinking about it. The behavior I see is consistent with my understanding of `finally` from other languages.

Basically, `finally` gives you a guarantee that it will actually run once the `try` block is exited. Likewise, `return` effectively assigns the return value and exits. But it doesn't (cannot and should not) breach the contract of try-finally, since the purpose of try-finally is to ensure resources are managed correctly, so it either exits to the caller or it exits to the finally block, depending on which one is most recent.

In your case, a return value is assigned and the `try` block is exited using `return`. We then have to continue into the `finally` block, since that is the core meaning of `finally` - we run it after we leave `try`. And then with `return`, we reassign the return value and finally leave the whole function. At this point, the return value is the second one that was assigned to it.

Maybe thinking of it like this is helpful, although I somewhat hope it isn't. You can see that "return" is reassigned before we have a chance to read it. I've simplified by removing any consideration of errors, but I console.logged the final output.

    //this is the function call at the end of your IIFP
    next_code.push(AfterMe)
    goto Anonymous

    // this is the function definition
    Anonymous:
        // this is the try-finally idiom
        next_code.push(FinallyBlock);
        //this is the try
        console.log("this try was executed");
        //these two lines are the first return
        var return = 'try block';
        goto next_code.pop();
        //this is the finally
        FinallyBlock:
            var return = 'finally block'
            goto next_code.pop();

    // this code gets executed from the FinallyBlock's goto and is as if you have a console.log(..) around your whole definition.
    AfterMe:
        console.log(result)


It's probably either this behavior or a statement in the finally block that doesn't run, even though it's guaranteed to. Either way, some assumption of normal program behavior is invalidated.

Unless one goes the PowerShell way and just forbids returning from finally.


This is how it's specified for anyone interested: https://stackoverflow.com/a/3838130/298073


What the hell?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: