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
allow narrowing to any sub-type-expression in type guards #6544
Currently X & Y | Z cannot be asserted as being X or Y separately in custom type guards (only X & Y together), however there seem to be no reason why they can't be. #6538
I am lacking proper terminology sorry. Out of all possible combinations of types A, ..., Z using operations of union and intersection a type guard asserting a value being of a separate constituent type should be allowed:
function isZ(value: (A | B) & D & (C | (E & F & G) | H) | (I & J)....): value is Z {
In other words: anything that, according to the type being assert, is possible should be able to be the target type of a custom type guard.
I think what I'm hearing is that if you have a parameter whose type is an intersection type, the type predicate's type must be assignable to any one constituent of the intersection type?
For example:
// OK because A is assignable to (A | B)declarefunctionf1(x: (A|B)&C): x is A// Errordeclarefunctionf2(x: (A|B)&C): x is number;
to sure I got it... not only intersection, but union too:
declare function f1(x: (A | B) & C): x is A; // ok
declare function f2(x: (A | B) & C): x is B; // ok
declare function f3(x: (A | B) & C): x is C; // ok
here! I got it: any sub-type-expression (as long as we talk unions and intersections) of the type-expression being asserted against should be a valid target of a type guard:
declare function f1(x: (A | B) & C): x is A | B; // ok
declare function f1(x: (A | B) & C): x is (A | B) & C; // ok
Currently X & Y | Z cannot be asserted as being X or Y separately in custom type guards (only X & Y together), however there seem to be no reason why they can't be.
Any value that satisfies (X & Y) | Z must satisfy at least one of the following:
it has all the members of Z
it has all the members of X AND all the members of Y
A value of type Z satisfies (1), so narrowing to Z makes sense.
However a value of type X satisfies neither (1) because it doesn't have all the members of Z, nor (2) because it doesn't have all the members of Y. Ditto for Y.
Isn't that the reason why X & Y | Z can't be asserted to X or to Y (but can to Z)?
Troy Gerwien (@yortus) I understand the mechanics of how it works now and you are right in what you saying. I am questioning the very premise that narrowing can't go beyond a member of a union type. I see no reason why it can't. What I am suggesting is that narrowing should be allowed to any sub-expression including lone terms.
Aleksey-Bykov maybe I get what you are saying, but I'm not sure. Do you mean that narrowing/type assertion should follow assignability rules sometimes, like:
declarefunctionf1(x: (A&B)): x is A;// error by narrowing rulesvarab: A&B;vara: A=ab;// ok by assignability rules
So if you want to narrow a variable of type X & Y | Z using a guard, so that you can treat it like an X in a guarded clause, then isn't it sufficient to just narrow it to X & Y? You can already treat a variable of type X & Y as if it were an X, no narrowing needed. Are there cases where this doesn't work?
the clarity is hurt, I will be asked by my peers why a function called isZ asserts to (X | Y) & Z
more isn't always good, when it comes to refactoring for example, I don't want my peers to use and rely on the extra information that wasn't meant to be there
having to type more than necessary
there is no reason for not doing it right (besides the budget)
Fair point. So while allowing narrowing X&Y to X seems pointless in the simple case (like 'narrowing' Dog to Animal), with more complex type expressions it makes type guards much clearer without diminishing type safety.
Activity
RyanCavanaugh commentedon Jan 19, 2016
Just as a starting point for things we need in the proposal, there's no such thing as "partial assignability".
zpdDG4gta8XKpMCd commentedon Jan 19, 2016
I am lacking proper terminology sorry. Out of all possible combinations of types A, ..., Z using operations of
unionandintersectiona type guard asserting a value being of a separate constituent type should be allowed:function isZ(value: (A | B) & D & (C | (E & F & G) | H) | (I & J)....): value is Z {In other words: anything that, according to the type being assert, is possible should be able to be the target type of a custom type guard.
RyanCavanaugh commentedon Jan 19, 2016
I think what I'm hearing is that if you have a parameter whose type is an intersection type, the type predicate's type must be assignable to any one constituent of the intersection type?
For example:
zpdDG4gta8XKpMCd commentedon Jan 19, 2016
to sure I got it... not only intersection, but union too:
here! I got it: any sub-type-expression (as long as we talk unions and intersections) of the type-expression being asserted against should be a valid target of a type guard:
RyanCavanaugh commentedon Jan 19, 2016
You don't need a special rule for union types because it's already the case that
Ais assignable toA | BzpdDG4gta8XKpMCd commentedon Jan 19, 2016
Because narrowing is what type guards do! 😜
yortus commentedon Jan 20, 2016
Any value that satisfies
(X & Y) | Zmust satisfy at least one of the following:ZXAND all the members ofYA value of type
Zsatisfies (1), so narrowing toZmakes sense.However a value of type
Xsatisfies neither (1) because it doesn't have all the members ofZ, nor (2) because it doesn't have all the members ofY. Ditto forY.Isn't that the reason why
X & Y | Zcan't be asserted toXor toY(but can toZ)?zpdDG4gta8XKpMCd commentedon Jan 20, 2016
Troy Gerwien (@yortus) I understand the mechanics of how it works now and you are right in what you saying. I am questioning the very premise that narrowing can't go beyond a member of a union type. I see no reason why it can't. What I am suggesting is that narrowing should be allowed to any sub-expression including lone terms.
yortus commentedon Jan 20, 2016
Aleksey-Bykov maybe I get what you are saying, but I'm not sure. Do you mean that narrowing/type assertion should follow assignability rules sometimes, like:
zpdDG4gta8XKpMCd commentedon Jan 20, 2016
yes
function f1(x: (A & B)): x is A;should be allowed as well asfunction f1(x: (A & B)): x is A & B;yortus commentedon Jan 20, 2016
So if you want to narrow a variable of type
X & Y | Zusing a guard, so that you can treat it like anXin a guarded clause, then isn't it sufficient to just narrow it toX & Y? You can already treat a variable of typeX & Yas if it were anX, no narrowing needed. Are there cases where this doesn't work?yortus commentedon Jan 20, 2016
The guard functions would be interesting:
zpdDG4gta8XKpMCd commentedon Jan 20, 2016
in your example it is
this is what brought it up in the first place (#6538)
Exactly, it's just a degenerate case.
yortus commentedon Jan 20, 2016
So why not do this instead:
zpdDG4gta8XKpMCd commentedon Jan 20, 2016
yes, you can do this way, the major points are:
isZasserts to(X | Y) & Zyortus commentedon Jan 20, 2016
Fair point. So while allowing narrowing
X&YtoXseems pointless in the simple case (like 'narrowing'DogtoAnimal), with more complex type expressions it makes type guards much clearer without diminishing type safety.mhegazy commentedon Feb 24, 2016
Type guard returns must be a subtype because they introduce new properties in their guarded expressions, not remove them