Microsoft/TypeScript

TypeScript cannot always infer the correct type through a user-defined type guard using the 'is' operator.

Open

#57.189 geöffnet am 26. Jan. 2024

Auf GitHub ansehen
 (4 Kommentare) (1 Reaktion) (0 zugewiesene Personen)TypeScript (6.726 Forks)batch import
BugDomain: This-TypingHelp Wanted

Repository-Metriken

Stars
 (48.455 Stars)
PR-Merge-Metriken
 (Durchschn. Merge 6T 17h) (9 gemergte PRs in 30 T)

Beschreibung

🔎 Search Terms

"typeguard", "is operator"

🕗 Version & Regression Information

Tested with typescript@5.1.3 and later

⏯ Playground Link

No response

💻 Code

enum kind {
	a = 'a',
	b = 'b',
	c = 'c'
}

type ID = {
	id: number
}

class Test<T extends kind> {
	value: T extends kind.a | kind.b ? ID : undefined
	type: T

	constructor(
		type: T,
		value: T extends kind.a | kind.b ? ID : undefined
	) {
		this.type = type
		this.value = value
	}

	hasID(): this is Test<Exclude<kind, kind.c>> {
		return this.type !== kind.c
	}

	do() {
		if (this.hasID()) {
			return this.value.id // Error: this.value: Object is possibly 'undefined'
			// return (this as Test<Exclude<kind, kind.c>>).value.id // this works
		}
	}
}

🙁 Actual behavior

TypeScript cannot infer that 'this' is of type 'Test<Exclude<kind, kind.c>>' even though there is a type guard. In the 'do' method, TypeScript throws a TSError stating that 'this.value': Object is possibly 'undefined'.

If we explicitly cast 'this' as 'Test<Exclude<kind, kind.c>>', we can access the 'value' attribute, and it works as it should. However, somehow the type guard does not have the same effect.

🙂 Expected behavior

Since there is a type guard around the 'this.value.id' line, TypeScript should handle 'this' as a 'Test<Exclude<kind, kind.c>>' type. At least, that's what type guards are for in my understanding.

Additional information about the issue

No response

Contributor Guide