You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm seeing surprising behavior when combining type guard methods (using the this is ... type predicate construct) with union types. It looks like if one branch of the union returns a predicate from a method while the other returns a boolean from a method with the same name, calling the method narrows the return type according to the predicate and loses the type information from the other branch.
🔎 Search Terms
predicate, guard, union, narrowing — there's a million results, particularly bugs related to #49625, but I wasn't able to find anything that looks closely connected and hasn't been fixed already.
This also seems kind of related to #50044, but I assume it's different since here the two types aren't subtypes of each other? Adding extra fields to differentiate the two types more also doesn't change the observed behavior at all.
🕗 Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about type guards/predicates
typeHasAttribute<T>=T&{attribute: number};classType1{attribute: number|null=null;predicate(): this is HasAttribute<Type1>{returntrue;}}classType2{attribute: number|null=null;predicate(): boolean{returntrue;}}functionassertType<T>(_val: T){}declareconstval: Type1|Type2;if(val.predicate()){assertType<number>(val.attribute);}
🙁 Actual behavior
val.attribute is number, because val is narrowed to HasAttribute<Type1> by the call to val.predicate().
Interestingly, if you change the definition in Type2 to predicate(): this is Type2, the type of val ends up as Type2 | Type1 | HasAttribute<Type1>.
🙂 Expected behavior
val.attribute is number | null, because val is narrowed to HasAttribute<Type1> | Type2 by the call to val.predicate(). The fact that predicate() returns this is HasAttribute<Type1> when called on Type1 shouldn't affect the Type2 branch.
The text was updated successfully, but these errors were encountered:
Yeah, our logic that constructs composite type predicates for union types isn't quite right. It simply ignores non-predicate function types. It should instead require that all union constituents have matching type predicates--and if not, the outcome shouldn't be considered a type predicate.
Uh oh!
There was an error while loading. Please reload this page.
Bug Report
I'm seeing surprising behavior when combining type guard methods (using the
this is ...
type predicate construct) with union types. It looks like if one branch of the union returns a predicate from a method while the other returns a boolean from a method with the same name, calling the method narrows the return type according to the predicate and loses the type information from the other branch.🔎 Search Terms
predicate, guard, union, narrowing — there's a million results, particularly bugs related to #49625, but I wasn't able to find anything that looks closely connected and hasn't been fixed already.
This also seems kind of related to #50044, but I assume it's different since here the two types aren't subtypes of each other? Adding extra fields to differentiate the two types more also doesn't change the observed behavior at all.
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
val.attribute
isnumber
, becauseval
is narrowed toHasAttribute<Type1>
by the call toval.predicate()
.Interestingly, if you change the definition in
Type2
topredicate(): this is Type2
, the type ofval
ends up asType2 | Type1 | HasAttribute<Type1>
.🙂 Expected behavior
val.attribute
isnumber | null
, becauseval
is narrowed toHasAttribute<Type1> | Type2
by the call toval.predicate()
. The fact thatpredicate()
returnsthis is HasAttribute<Type1>
when called onType1
shouldn't affect theType2
branch.The text was updated successfully, but these errors were encountered: