microsoft / microsoft/TypeScript

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

Open
#57,189 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Domain: This-Typing Help Wanted
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied TypeScript reproduction and trace how the type checker narrows this after the hasID() user-defined type guard. Add a compiler regression test for the expected this.value.id access, then verify that the diagnostic is removed without weakening checks for the kind.c case.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.