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
Errors issued for working code due to narrowing behaviour #7160
interfaceThing{thing: string;}functionisThing(x: any): x is Thing{returnx&&typeofx.thing==='string';}functionfoo(x: Thing|string){letbranch: number;letresult: string;if(typeofx==="string"){branch=1;result=x;// OKif(isThing(x)){branch=2;result=x.thing;// ERROR: 'thing' does not exist on type 'string'}}else{branch=3;result=x.thing;// OKif(typeofx==="string"){branch=4;result=x// ERROR: 'Thing|string' is not assignable to type 'string'}}return`(${branch}) ${result}`;}leta=foo('string a');letb=foo({thing: 'thing b'});String.prototype['thing']='thing c';letc=foo('string c');console.log(a,b,c);// outputs "(1) string a (3) thing b (2) thing c"
Expected behavior:
No compile-time or runtime errors, and outputs "(1) string a (3) thing b (2) thing c"
Actual behavior:
No runtime errors, and outputs "(1) string a (3) thing b (2) thing c" as expected, but compiler issues two errors as shown in code comments.
changed the title [-]Errors issued for working code due to narrowing bahaviour[/-][+]Errors issued for working code due to narrowing behaviour[/+]on Feb 20, 2016
In branches 2 and 4, the compiler could safely infer that x is of type Thing&string. Is that feasible? That would match the runtime semantics and get rid of the compiler errors.
// Overlapping interfaces, neither is a 'subtype' of the other.// These might be used for cross-cutting feature/capability detection.interfaceAB{a;b;}interfaceBC{b;c;}functionisAB(x: any): x is AB{returnx&&x.a&&x.b;}functionisBC(x: any): x is BC{returnx&&x.b&&x.c;}interfaceOther{d?;e?;f?}// A function that works with objects with varying capabilities...functiontest(x: AB|BC|(AB&BC)|Other){if(isAB(x)){if(isBC(x)){// This is where AB and BC overlap, so we must have an AB&BC// This works fine at runtime, but not at compile time.console.log(x.a,x.b,x.c);// ERROR: Property 'c' does not exist on 'AB'}else{console.log(x.a,x.b);}}else{console.log('other');}}// Runtime results:leta='A',b='B',c='C';test({a});// othertest({b});// othertest({c});// othertest({a,b});// A Btest({a,c});// othertest({b,c});// othertest({a,b,c});// A B C
As for the original example, it was the result of me trying to work with the code in this comment, which was presented as the explanation for #7045, but looked out of step with runtime behaviour to me.
Yes, the OP example now works, but the second example still doesn't compile (but is runtime-valid code).
I take it this fix is due to Wesley Wigham (@weswigham)'s work in #8993, which resolves the OP problem through primitive narrowing. The second example is really a different problem with no primitives involved. I'll re-post that as a separate issue.
This is a really helpful improvement to type guards, thanks.
Activity
[-]Errors issued for working code due to narrowing bahaviour[/-][+]Errors issued for working code due to narrowing behaviour[/+]yortus commentedon Feb 20, 2016
In branches 2 and 4, the compiler could safely infer that
xis of typeThing&string. Is that feasible? That would match the runtime semantics and get rid of the compiler errors.DanielRosenwasser commentedon Feb 20, 2016
Technically, the value for
xmight change if captured and modified by a closure. This could fit in a broader category of data flow analysis though.DanielRosenwasser commentedon Feb 20, 2016
Do you have a compelling use case? I don't see you actually modifying
x, so why are you performing those nested checks?yortus commentedon Mar 2, 2016
Hi Daniel Rosenwasser (@DanielRosenwasser), sorry I lost track of this for a while. Is this example any clearer?
As for the original example, it was the result of me trying to work with the code in this comment, which was presented as the explanation for #7045, but looked out of step with runtime behaviour to me.
yortus commentedon Apr 26, 2016
I was hoping this way of using type guards might work now that #8010 has landed, but it still doesn't :(
Is there any hope for this 'feature-detection' style of type guards to get better type checking?
mhegazy commentedon Jun 7, 2016
This should be working as intended in latest.
yortus commentedon Jun 8, 2016
Yes, the OP example now works, but the second example still doesn't compile (but is runtime-valid code).
I take it this fix is due to Wesley Wigham (@weswigham)'s work in #8993, which resolves the OP problem through primitive narrowing. The second example is really a different problem with no primitives involved. I'll re-post that as a separate issue.
This is a really helpful improvement to type guards, thanks.
yortus commentedon Jun 9, 2016
The second example is now fixed via #9031.