Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generalize never type handling for control flow analysis based type guard #12825

Open
vilicvane opened this issue Dec 10, 2016 · 11 comments
Open
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@vilicvane
Copy link
Contributor

I guess there would be some duplicates, but I cannot find it.

TypeScript Version: 2.1.14

Code

function throwError(): never {
    throw new Error();
}

let foo: string | undefined;

if (!foo) {
    throwError();
}

foo; // Expected to be `string` instead of `string | undefined`.
@akarzazi
Copy link

akarzazi commented Dec 10, 2016

It would be a great way to control the flow.

For now, you may use return if your are inside a function.

function throwError(): never {
    throw new Error();
}

let foo: string | undefined;
if (!foo) {
    throwError();
    return:
}
foo; // foo is string

or use a check typeguard

function throwError(): never {
    throw new Error();
}

// Inferred return type is T
function check<T>(x: T | undefined) {
    return x || throwError();
}

let foo: string | undefined;

let foosafe = check(foo); // foosafe is string

@yortus
Copy link
Contributor

yortus commented Dec 11, 2016

Related: #8655

@dhedey
Copy link

dhedey commented Aug 14, 2017

I'd like to echo my support for this. Currently to get control flow analysis to work correctly in our codebase (which make use of a framework requiring the use of an external throwError style method), we explicitly throw ''; // See https://github.com/Microsoft/TypeScript/issues/12825 in the following line.

ADDENDUM:

I've just noted RyanCavanaugh's comment on a duplicate thread.

The recommended workaround is to write return throwError();

We'll use this workaround from now on. It appears to not interfere with the returned type. 👍

if (!foo) {
    return throwError();
}

@masaeedu
Copy link
Contributor

I currently use const guaranteedFoo = foo || throwError() to strip away undefined from stuff that I know should never be undefined.

@parzh
Copy link

parzh commented Apr 15, 2018

@masaeedu In that way you strip away not only undefined but every other "falsey" values, like 0, "", false, null and NaN.

@masaeedu
Copy link
Contributor

@parzh Depends on what your value's type is. If the type is { foo: string } | undefined, you already know it's not 0, false, or any of those other things. If you do indeed have a mixed type that could contain booleans and strings and a multitude of other things, you should do a more specific check: foo !== undefined || throwError().

@ypresto
Copy link
Contributor

ypresto commented Aug 30, 2018

I got Object is possibly 'undefined' error after try { ... } catch (e) { ...; process.exit(1) } in node.
I expected that process.exit(1) behave like throw for type inference.

@simonbuchan
Copy link

For other people googling this, this issue was addressed in #14490. Basically it seems the issue is TS currently has separate passes for flow control and type assignment, and the former needs to run first for the latter to work, and there didn't seem to be any clean solutions that would also handle imported functions, etc....
So the current workaround is just return neverReturningFunction();, which won't alter the return type and lets flow control do its thing.

@Kinrany
Copy link

Kinrany commented Mar 21, 2021

The workaround doesn't work in top-level code outside functions, since there's no return there.

@fcole90
Copy link

fcole90 commented Aug 11, 2021

@Kinrany you can still use the other workaround val !== undefined || throw new Error()

@jakebailey
Copy link
Member

The original code in this issue has been working as expected for a while. Is there anything else above that isn't right? I'm thinking no, and that this issue can be closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests