Repository: TypeScript
Issue: Exhaustiveness checking against an enum only works when the enum has >1 member.
Labels: Bug

<!-- Please try to reproduce the issue with `typescript@next`. It may have already been fixed. -->
**TypeScript Version:**  typescript@2.9.0-dev.20180420

<!-- Search terms you tried before logging this (so others can find this issue more easily) -->
**Search Terms:** discriminated, exhaustiveness, type guard, narrowing

**Code**

```ts
// Legal action types for ValidAction
enum ActionTypes {
  INCREMENT = 'INCREMENT',
//   DECREMENT = 'DECREMENT',
}

interface IIncrement {
  payload: {};
  type: ActionTypes.INCREMENT;
}

// interface IDecrement {
//   payload: {};
//   type: ActionTypes.DECREMENT;
// }

// Any string not present in T
type AnyStringExcept<T extends string> = { [P in T]: never; };

// ValidAction is an interface with a type in ActionTypes
type ValidAction = IIncrement;
// type ValidAction = IIncrement | IDecrement;
// UnhandledAction in an interface with a type that is not within ActionTypes
type UnhandledAction = { type: AnyStringExcept<ActionTypes>; };

// The set of all actions
type PossibleAction = ValidAction | UnhandledAction;

// Discriminates to ValidAction
function isUnhandled(x: PossibleAction): x is UnhandledAction {
    return !(x.type in ActionTypes);
}

type CounterState = number;
const initialState: CounterState = 0;

function receiveAction(state = initialState, action: PossibleAction) {
    // typeof action === PossibleAction
    if (isUnhandled(action)) {
        // typeof action === UnhandledAction
        return state;
    }

    // typeof action === ValidAction
    switch (action.type) {
        case ActionTypes.INCREMENT:
            // typeof action === IIncrement
            return state + 1;
        // case ActionTypes.DECREMENT:
        //     return state - 1;
    }

    // typeof action === IIncrement
    // Since INCREMENT is handled above, this should be impossible,
    // However the compiler will say that assertNever cannot receive an argument of type IIncrement
    return assertNever(action);
}

function assertNever(x: UnhandledAction): never {
    throw new Error(`Unhandled action type: ${x.type}`);
}
```

**Expected behavior:** No error would be thrown, as the switch statement is exhaustive. If the ActionTypes.DECREMENT parts are uncommented (resulting in two possible values for ActionTypes) there is no error. An error only occurs when ActionTypes takes on a single value. The error occurs even if the `never` assertion happens in the default statement, which is obviously unreachable from IIncrement.

**Actual behavior:** An error is thrown despite the only possible value being explicitly handled. If ActionTypes.DECREMENT is uncommented the expected behavior is present.

**Playground Link:** (fixed the links)
[Error](https://www.typescriptlang.org/play/index.html#src=%2F%2F%20Legal%20action%20types%20for%20ValidAction%0D%0Aenum%20ActionTypes%20%7B%0D%0A%20%20INCREMENT%20%3D%20'INCREMENT'%2C%0D%0A%2F%2F%20%20%20DECREMENT%20%3D%20'DECREMENT'%2C%0D%0A%7D%0D%0A%0D%0Ainterface%20IIncrement%20%7B%0D%0A%20%20payload%3A%20%7B%7D%3B%0D%0A%20%20type%3A%20ActionTypes.INCREMENT%3B%0D%0A%7D%0D%0A%0D%0A%2F%2F%20interface%20IDecrement%20%7B%0D%0A%2F%2F%20%20%20payload%3A%20%7B%7D%3B%0D%0A%2F%2F%20%20%20type%3A%20ActionTypes.DECREMENT%3B%0D%0A%2F%2F%20%7D%0D%0A%0D%0A%2F%2F%20Any%20string%20not%20present%20in%20T%0D%0Atype%20AnyStringExcept%3CT%20extends%20string%3E%20%3D%20%7B%20%5BP%20in%20T%5D%3A%20never%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20ValidAction%20is%20an%20interface%20with%20a%20type%20in%20ActionTypes%0D%0Atype%20ValidAction%20%3D%20IIncrement%3B%0D%0A%2F%2F%20type%20ValidAction%20%3D%20IIncrement%20%7C%20IDecrement%3B%0D%0A%0D%0A%2F%2F%20UnhandledAction%20in%20an%20interface%20with%20a%20type%20that%20is%20not%20within%20ActionTypes%0D%0Atype%20UnhandledAction%20%3D%20%7B%20type%3A%20AnyStringExcept%3CActionTypes%3E%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20The%20set%20of%20all%20actions%0D%0Atype%20PossibleAction%20%3D%20ValidAction%20%7C%20UnhandledAction%3B%0D%0A%0D%0A%2F%2F%20Discriminates%20to%20ValidAction%0D%0Afunction%20isUnhandled(x%3A%20PossibleAction)%3A%20x%20is%20UnhandledAction%20%7B%0D%0A%20%20%20%20return%20!(x.type%20in%20ActionTypes)%3B%0D%0A%7D%0D%0A%0D%0Atype%20CounterState%20%3D%20number%3B%0D%0Aconst%20initialState%3A%20CounterState%20%3D%200%3B%0D%0A%0D%0Afunction%20receiveAction(state%20%3D%20initialState%2C%20action%3A%20PossibleAction)%20%7B%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20PossibleAction%0D%0A%20%20%20%20if%20(isUnhandled(action))%20%7B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20UnhandledAction%0D%0A%20%20%20%20%20%20%20%20return%20state%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20ValidAction%0D%0A%20%20%20%20switch%20(action.type)%20%7B%0D%0A%20%20%20%20%20%20%20%20case%20ActionTypes.INCREMENT%3A%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20IIncrement%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20state%20%2B%201%3B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20case%20ActionTypes.DECREMENT%3A%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20%20%20%20%20return%20state%20-%201%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20IIncrement%0D%0A%20%20%20%20%2F%2F%20Since%20INCREMENT%20is%20handled%20above%2C%20this%20should%20be%20impossible%2C%0D%0A%20%20%20%20%2F%2F%20However%20the%20compiler%20will%20say%20that%20assertNever%20cannot%20receive%20an%20argument%20of%20type%20IIncrement%0D%0A%20%20%20%20return%20assertNever(action)%3B%0D%0A%7D%0D%0A%0D%0Afunction%20assertNever(x%3A%20UnhandledAction)%3A%20never%20%7B%0D%0A%20%20%20%20throw%20new%20Error(%60Unhandled%20action%20type%3A%20%24%7Bx.type%7D%60)%3B%0D%0A%7D)
[Working](https://www.typescriptlang.org/play/index.html#src=%2F%2F%20Legal%20action%20types%20for%20ValidAction%0D%0Aenum%20ActionTypes%20%7B%0D%0A%20%20INCREMENT%20%3D%20'INCREMENT'%2C%0D%0A%20%20DECREMENT%20%3D%20'DECREMENT'%2C%0D%0A%7D%0D%0A%0D%0Ainterface%20IIncrement%20%7B%0D%0A%20%20payload%3A%20%7B%7D%3B%0D%0A%20%20type%3A%20ActionTypes.INCREMENT%3B%0D%0A%7D%0D%0A%0D%0Ainterface%20IDecrement%20%7B%0D%0A%20%20payload%3A%20%7B%7D%3B%0D%0A%20%20type%3A%20ActionTypes.DECREMENT%3B%0D%0A%7D%0D%0A%0D%0A%2F%2F%20Any%20string%20not%20present%20in%20T%0D%0Atype%20AnyStringExcept%3CT%20extends%20string%3E%20%3D%20%7B%20%5BP%20in%20T%5D%3A%20never%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20ValidAction%20is%20an%20interface%20with%20a%20type%20in%20ActionTypes%0D%0A%2F%2F%20type%20ValidAction%20%3D%20IIncrement%3B%0D%0Atype%20ValidAction%20%3D%20IIncrement%20%7C%20IDecrement%3B%0D%0A%0D%0A%2F%2F%20UnhandledAction%20in%20an%20interface%20with%20a%20type%20that%20is%20not%20within%20ActionTypes%0D%0Atype%20UnhandledAction%20%3D%20%7B%20type%3A%20AnyStringExcept%3CActionTypes%3E%3B%20%7D%3B%0D%0A%0D%0A%2F%2F%20The%20set%20of%20all%20actions%0D%0Atype%20PossibleAction%20%3D%20ValidAction%20%7C%20UnhandledAction%3B%0D%0A%0D%0A%2F%2F%20Discriminates%20to%20ValidAction%0D%0Afunction%20isUnhandled(x%3A%20PossibleAction)%3A%20x%20is%20UnhandledAction%20%7B%0D%0A%20%20%20%20return%20!(x.type%20in%20ActionTypes)%3B%0D%0A%7D%0D%0A%0D%0Atype%20CounterState%20%3D%20number%3B%0D%0Aconst%20initialState%3A%20CounterState%20%3D%200%3B%0D%0A%0D%0Afunction%20receiveAction(state%20%3D%20initialState%2C%20action%3A%20PossibleAction)%20%7B%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20PossibleAction%0D%0A%20%20%20%20if%20(isUnhandled(action))%20%7B%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20UnhandledAction%0D%0A%20%20%20%20%20%20%20%20return%20state%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20ValidAction%0D%0A%20%20%20%20switch%20(action.type)%20%7B%0D%0A%20%20%20%20%20%20%20%20case%20ActionTypes.INCREMENT%3A%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20IIncrement%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20state%20%2B%201%3B%0D%0A%20%20%20%20%20%20%20%20case%20ActionTypes.DECREMENT%3A%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20state%20-%201%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20%2F%2F%20typeof%20action%20%3D%3D%3D%20IIncrement%0D%0A%20%20%20%20%2F%2F%20Since%20INCREMENT%20is%20handled%20above%2C%20this%20should%20be%20impossible%2C%0D%0A%20%20%20%20%2F%2F%20However%20the%20compiler%20will%20say%20that%20assertNever%20cannot%20receive%20an%20argument%20of%20type%20IIncrement%0D%0A%20%20%20%20return%20assertNever(action)%3B%0D%0A%7D%0D%0A%0D%0Afunction%20assertNever(x%3A%20UnhandledAction)%3A%20never%20%7B%0D%0A%20%20%20%20throw%20new%20Error(%60Unhandled%20action%20type%3A%20%24%7Bx.type%7D%60)%3B%0D%0A%7D)

**Related Issues:**
https://github.com/Microsoft/TypeScript/issues/19904
https://github.com/Microsoft/TypeScript/issues/14210
https://github.com/Microsoft/TypeScript/issues/18056