microsoft/TypeScript

Exhaustive checks not working correctly with switch statements

Closed this issue ยท 4 comments

j2fyi commented

๐Ÿ”Ž Search Terms

exhaustiveness, switch statement, discriminated union

๐Ÿ•— Version & Regression Information

  • This is the behavior in every version I tried in the typescript playground, and I reviewed the FAQ for entries about the exhaustive checks in switch statements

โฏ Playground Link

https://www.typescriptlang.org/play/?ts=6.0.0-dev.20251028#code/C4TwDgpgBAyglgOwOYBsIFEBOmoF4oDeAUAJAD6E2AXAEQwCSAcgOIAy6NRAvkUaJFACyAVxTA4WHPmLlKmWgDF6AJRgAVTlygAfQqQrU66AMIB5RgBFNAbl4AzYQgDG4gPYIoagBZwAzgoBDOBRfAAoAShkAEwgnFADMaCd3X2AoCCp4ZDRJW1JfAHc4YCcvUIgAOgNMSNISJwDfaDomNg4qGRISROBhTA8WlnYydGVlU2VOLp4umLsA0WAOuvqUtIgADy8F1LgANwyoBAgDqXTbLpIAeiuAPX5oPyhdlBQoRDs5CCioRthEVAYbArYBeTCuApHCCQySuTChAAGAFUENsEFE0FEqFAACQETbbYS7A5cBHhOozHg8IgOZxuDzePwAdThAGswrUSDE4gkkmt0lQRGIJNg8iRCsVSuUqnJOfVGs0lKoNMtLj0+gMFHBMKl0tg4VMSDN5U0oEYzJYaKquur+maYLF3D85AaKXU5gsxNbVghdQSduIDtjjqc8OcQWCIVCYfr4cjUQF0ZjsXj-UTAxBSeTpqQqbwgA

๐Ÿ’ป Code

type SingleErr = {
	_err:"SINGLE"
}

type MultiErr = {
	_err:"FIRST"
} | {
	_err:"SECOND"
};

function ThisFails(){
	declare const e:SingleErr;

	switch(e._err){
		case "SINGLE":{
			return "SINGLE_ERROR"
		}
		default:{
			const exhaustive: never = e;
			//^type is still inferred as SingleErr
			throw new Error(`Unhandled: ${exhaustive}`)
		}
	}
}

function ThisWorks(){
	declare const e:MultiErr;

	switch(e._err){
		case "FIRST":{
			return "First error"
		}
		case "SECOND":{
			return "Second error"
		}
		default:{
			const exhaustive: never = e;
			throw new Error(`Unhandled: ${exhaustive}`)
		}
	}
}

๐Ÿ™ Actual behavior

In ThisFails(), after handling the only possible case ("SINGLE"), TypeScript does not narrow the type to never in the default branch. The variable e retains its type SingleErr, causing a type error when trying to assign it to a never type.

๐Ÿ™‚ Expected behavior

TypeScript should recognize that all possible cases have been exhausted in the switch statement and narrow e to never in the default branch, consistent with how it handles multi-member discriminated unions. This would enable proper exhaustiveness checking for single-member discriminated types.

Additional information about the issue

This inconsistency makes it difficult to write exhaustiveness checks that work uniformly across discriminated types, regardless of whether they have one or multiple members. The workaround is to always use union types with at least two members, but this seems like a bug that could be solved in ts.

Duplicate of #18056.

๐Ÿค– Thank you for your issue! I've done some analysis to help get you started. This response is automatically generated; feel free to ๐Ÿ‘ or ๐Ÿ‘Ž this comment according to its usefulness.

Similar Issues

Here are the most similar issues I found

If your issue is a duplicate of one of these, feel free to close this issue. Otherwise, no action is needed.

I guess I should make a FAQ entry

j2fyi commented

Seems like this was already a known issue and the most similar one is: #16976. Closing this for now. Thanks!