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
{{ message }}
Repository navigation
Narrowing does not work when done via a predicate that narrows via mapped typesΒ #47283
Narrowing a union through a UDTG goes through this algorithm (roughly; this is from memory/experimentally-obtained):
Compute the type that's narrowed to (this step works, which you can verify by changing t is { ... to { ...
See if the narrowed-to type is a subtype of at least one constituent of the union
If yes, narrow to those constituents(s) of the union
Otherwise, intersect the narrowed-type type with each member of the union
I will name this behavior "filter-first"
Not using this algorithm produces counterintuitive and unhelpful results
typeCat={meow: any};typeDog={woof: any};typeChipped={id: number};declarefunctionisCat(c: any): c is Cat;declareletcd: Cat|Dog;if(isCat(cd)){cd;// ^?// Desired & actual cd: Cat// Without filter-first: (Cat) | (Cat & Dog)}declarefunctionisChipped(c: any): c is Chipped;if(isChipped(cd)){// Desired and actual cd: (Cat | Dog) & Chipped// Without filter-first: never}
I get the need for pragmatism but I think this approach is a little sub-optimal. Instead of "filter-first"ing what we could do is intersect the type to be narrowed with the narrowed type and then pragmatically make Dog & Cat as never as there is no overlap so pragmatically the intersection truly is never. And I'm not sure why in the second chunk you say without filter-first it'd be never but with my suggestion it'd be the desired type.
(Cat|Dog)&Cat=(Cat&Cat)|(Dog&Cat)=Cat|never=Cat// desired result(Cat|Dog)&Chipped// desired result
But maybe this will bring up other problems idk haha.
Actually I didn't have this exact issue posted, my issue was this failing test but turns out my NotAwareIntesect<A, B> was buggy which I fixed and now the test is passing.
So basically I'm fine with the current filter-filter approach haha, but I still think it might be a little sub-optimal.
Intersection cannot produce never unless the set is actually provably empty, which it's not.
Yes I'm aware hence "pragmatically" produce never only for UDTP, not everywhere ofc that would be ridiculous.
Also I realized making intersection of non-overlapping types never won't work because then (Cat | Dog) & Chipped also would become never. So we'd do this only when one of the narrowed constituents is a subtype of one of the to-narrow constituents. This similar to first-filter except the OP issue also gets fixed. Let me show you a demo...
Do you have cases where this algorithm would produce undesired results?
I hope you see that this algorithm isn't arbitrary, narrowing by definition is intersecting narrowed with to-narrow and hence (Cat | Dog) & Cat is correct but TypeScript does some magic to make it Cat which is good magic, but what I'm saying is we can do that magic in a way that doesn't mess other cases like OP (ie Test2).
Ryan Cavanaugh (@RyanCavanaugh) should I open a new issue with feature request template if I want this to be considered or discussed? (I'm looking for cases where my algorithm would produce undesired results)
Activity
RyanCavanaugh commentedon Jan 3, 2022
Narrowing a union through a UDTG goes through this algorithm (roughly; this is from memory/experimentally-obtained):
t is { ...to{ ...I will name this behavior "filter-first"
Not using this algorithm produces counterintuitive and unhelpful results
devanshj commentedon Jan 5, 2022
I get the need for pragmatism but I think this approach is a little sub-optimal. Instead of "filter-first"ing what we could do is intersect the type to be narrowed with the narrowed type and then pragmatically make
Dog & Catasneveras there is no overlap so pragmatically the intersection truly isnever. And I'm not sure why in the second chunk you say without filter-first it'd beneverbut with my suggestion it'd be the desired type.But maybe this will bring up other problems idk haha.
Actually I didn't have this exact issue posted, my issue was this failing test but turns out my
NotAwareIntesect<A, B>was buggy which I fixed and now the test is passing.So basically I'm fine with the current filter-filter approach haha, but I still think it might be a little sub-optimal.
RyanCavanaugh commentedon Jan 5, 2022
Intersection cannot produce
neverunless the set is actually provably empty, which it's not. This happens and matters.devanshj commentedon Jan 6, 2022
Yes I'm aware hence "pragmatically" produce
neveronly for UDTP, not everywhere ofc that would be ridiculous.Also I realized making intersection of non-overlapping types
neverwon't work because then(Cat | Dog) & Chippedalso would becomenever. So we'd do this only when one of the narrowed constituents is a subtype of one of the to-narrow constituents. This similar to first-filter except the OP issue also gets fixed. Let me show you a demo...Do you have cases where this algorithm would produce undesired results?
I hope you see that this algorithm isn't arbitrary, narrowing by definition is intersecting narrowed with to-narrow and hence
(Cat | Dog) & Catis correct but TypeScript does some magic to make itCatwhich is good magic, but what I'm saying is we can do that magic in a way that doesn't mess other cases like OP (ieTest2).devanshj commentedon Jan 6, 2022
Also, it's important to note that the
{ b: string }narrowing, if not wrong, at least leads to runtime errors...With my narrowing algorithm this would have not compiled.
devanshj commentedon Jan 8, 2022
Ryan Cavanaugh (@RyanCavanaugh) should I open a new issue with feature request template if I want this to be considered or discussed? (I'm looking for cases where my algorithm would produce undesired results)
RyanCavanaugh commentedon Jan 11, 2022
Devansh Jethmalani (@devanshj) yes, new issue please. I'm not quite clear on what you're proposing.
devanshj commentedon Jan 11, 2022
Done #47389