question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Type parameter constrained to union cannot be exhaustively narrowed to 'never'

See original GitHub issue

TypeScript Version: 2.1.1 (http://www.typescriptlang.org/play/index.html)

Code

type Type = "a" | "b"

function isA(a: any): a is "a" {
    return a === "a"
}

function isB(a: any): a is "b" {
    return a === "b"
}

function assertNever(arg: never) {
    throw new Error("This should never be called")
}

function handleAction<T extends Type>(type: T) {
    if (isA(type)) {
        return type
    } else if (isB(type)) {
        return type
    } else {
        assertNever(type)
    }
}

Expected behavior: No compilation error

Actual behavior: Argument of type ‘T’ is not assignable to type ‘never’

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:3
  • Comments:11 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
OliverJAshcommented, Jan 20, 2017

I have a problem which I think is related to this.

{
    type FooOrBar = "foo" | "bar"

    let x: FooOrBar
    (() => { // Error: Not all code paths return a value.
        if (x === 'foo') {
            x
            return 1
        } else if (x === 'bar') {
            x
            return 2;
        }
    })
}

TS should be smart enough to know I have exhausted the union here.

Is this related?

0reactions
arxanascommented, Sep 16, 2019

I believe this is the same issue:

type Foo = "bar" | "baz";

function okay(foo: Foo): number {
    switch (foo) {
        case "bar":
            return 1;
        case "baz":
            return 2;
    }
}

// Function lacks ending return statement and return type does not include 'undefined'.
function illegal<T extends Foo>(foo: T): number {
    switch (foo) {
        case "bar":
            return 1;
        case "baz":
            return 2;
    }
}

This is useful in practice. My actual use-case involves a type projection like function foo<T extends Id>(id: T, assocatedData: AssociatedData[T]>), like this:

const data = {
  "foo": {},
  "bar": {},
};

type AssociatedData = {
    "foo": {
        name: string,
    },
    "bar": {
        id: number,
    },
};

type Id = keyof typeof data;

function fancy<T extends Id>(id: T, data: AssociatedData[T]): number {
    // Not detected as exhaustive.
    switch (id) {
        case "foo":
            return 1;
        case "bar":
            return 2;
    }
}

(NB: the type projection does not refine types exactly how I would like here, but that’s a separate issue.)

Read more comments on GitHub >

github_iconTop Results From Across the Web

Generic type extending union is not narrowed by type guard
In theory it would be sound to narrow T to something like "a type which extends string | number but whose intersection with...
Read more >
A complete guide to TypeScript's never type - Zhenghao
Inadmissible parameters in generics and functions. Intersection of incompatible types. An empty union (a union type of nothingness). The return ...
Read more >
Documentation - Narrowing - TypeScript
If we'd assigned a boolean to x , we'd have seen an error since that wasn't part of the declared type. x =...
Read more >
Widening and Narrowing in Typescript | manual - GitHub Pages
The types null and undefined are converted to any . This happens recursively in object types, union types, and array types (including tuples)....
Read more >
Really Advanced Typescript Types - Tableau Engineering Blog
Usually. I don't know how many hours I've wasted in life reading Java code that throws when constructor parameters are null , never...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found