Skip to content

Exhaustiveness checking against an enum only works when the enum has >1 member.#23572

@confusingstraw

Description

@confusingstraw

TypeScript Version:[email protected]

Search Terms: discriminated, exhaustiveness, type guard, narrowing

Code

// Legal action types for ValidActionenumActionTypes{INCREMENT='INCREMENT',// DECREMENT = 'DECREMENT',}interfaceIIncrement{payload: {};type: ActionTypes.INCREMENT;}// interface IDecrement{// payload:{};// type: ActionTypes.DECREMENT;// }// Any string not present in TtypeAnyStringExcept<Textendsstring>={[PinT]: never;};// ValidAction is an interface with a type in ActionTypestypeValidAction=IIncrement;// type ValidAction = IIncrement | IDecrement;// UnhandledAction in an interface with a type that is not within ActionTypestypeUnhandledAction={type: AnyStringExcept<ActionTypes>;};// The set of all actionstypePossibleAction=ValidAction|UnhandledAction;// Discriminates to ValidActionfunctionisUnhandled(x: PossibleAction): x is UnhandledAction{return!(x.typeinActionTypes);}typeCounterState=number;constinitialState: CounterState=0;functionreceiveAction(state=initialState,action: PossibleAction){// typeof action === PossibleActionif(isUnhandled(action)){// typeof action === UnhandledActionreturnstate;}// typeof action === ValidActionswitch(action.type){caseActionTypes.INCREMENT: // typeof action === IIncrementreturnstate+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 IIncrementreturnassertNever(action);}functionassertNever(x: UnhandledAction): never{thrownewError(`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
Working

Related Issues:
#19904
#14210
#18056

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptDomain: check: Control FlowThe issue relates to control flow analysisFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions