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.

Generic type unnarrowed by `switch...case`

See original GitHub issue

Bug Report

🔎 Search Terms

  • narrowing of generic types
  • exhaustive switch case

🕗 Version & Regression Information

Theoretically this should be fixed by:

Similar issue:

I’m trying against the latest beta: 4.3.0-pr-43183-11 / 15fae38b39a370f1f597606e552dd40998a1ac49.

⏯ Playground Link

Playground link with relevant code

💻 Code

interface TopLevelElementMap {
	"a": HTMLElement;
	"b": HTMLElement;
}

interface ElementAttributes {}

interface ElementAttributesMap {
	"a": ElementAttributes;
	"b": ElementAttributes;
}

class Element<TagName extends keyof TopLevelElementMap> {
	protected attributes: ElementAttributesMap[TagName] = {};

	public constructor(type: TagName) {}
}

const ElementTagNameMap = {
	"a": function(): Element<"a"> {
		return new Element("a");
	},
	"b": function(): Element<"b"> {
		return new Element("b");
	}
};

function createPrimitive<TagName extends keyof typeof ElementTagNameMap>(tagName: TagName) {
	switch (tagName) {
		case "a":
			return function(): Element<TagName> {
				//
				// [?] Shouldn't `tagName` get narrowed to just "a"?
				//
				return ElementTagNameMap[tagName]();
			};

		case "b":

		default:
			throw new Error("Unrecognized element `" + tagName + "`.");
	}
}

const a = createPrimitive("a");
const b = createPrimitive("b");

As a workaround I can assert as Element<TagName> on the inner-most return statement to silence the error.

🙁 Actual behavior

Type 'Element<"a"> | Element<"b">' is not assignable to type 'Element<TagName>'.
  Type 'Element<"a">' is not assignable to type 'Element<TagName>'.
    Type '"a"' is not assignable to type 'TagName'.
      '"a"' is assignable to the constraint of type 'TagName', but 'TagName' could be instantiated with a different subtype of constraint '"a" | "b"'. (2322)

🙂 Expected behavior

TagName should be narrowed to one of the possible values dictated by the case clause.

Issue Analytics

  • State:open
  • Created 2 years ago
  • Reactions:4
  • Comments:10 (3 by maintainers)

github_iconTop GitHub Comments

3reactions
mkantorcommented, Jul 1, 2021

Is there a more targeted issue for just this problem?

  • e[x] when x is a type parameter constrained to K1 | K2 and K1 and K2 are both valid keys of e should be legally narrowed via a contextual type

In case it helps, here’s a simpler situation that demonstrates it without the other problems mentioned by @RyanCavanaugh:

const stuff = {
    string: '',
    number: 0,
}
function f<T extends 'string' | 'number'>(x: T): void {
    if (x === 'string') {
        stuff[x].length // error :(
    }
}
0reactions
ouuancommented, Aug 26, 2021

I found a case that could be relevant: Playground link.

I’ve confirmed that in this piece of code, const a: "a" = x, takesA(x) and const never: never = x have errors in TypeScript v4.2.3. But in v4.3.5, only obj[x] have an error. This could be the result of #13995 being partially fixed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Narrowing types using switch statement does not work on a ...
On example 1, I am trying to narrow down a generic type which extends an enum. However it still errors when I use...
Read more >
ouuan on Twitter: "我看不懂了... #TypeScript https://t.co ...
Generic type unnarrowed by `switch...case` · Issue #43873 · microsoft/TypeScript. Bug Report Search Terms narrowing of generic types ...
Read more >
Determining object type of a Generic Type using the Switch ...
This generic function should be able to take in a generic type and execute code based on the generic type passed into the...
Read more >
Variables - Emacs Online Documentation
... bibtex-autokey-name-case-convert · bibtex-autokey-name-change-strings · bibtex-autokey-name- ... cc-imenu-objc-generic-expression-general-func-index ...
Read more >
The Org Manual - Org mode
Note that in this case, ' make autoloads ' is mandatory: it defines Org's ... Switch back to the startup visibility of the...
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