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
typeHandledResult={data: any,errors?: any}typeUnhandledResult={message: string}constresult=nullasanyasHandledResult|UnhandledResultconstnoWork=(): HandledResult=>{if('data'inresult||'errors'inresult)returnresult// when checking an optional property second, I get an error return{data: null,errors: [result.message]}}constwork=(): HandledResult=>{if('errors'inresult||'data'inresult)returnresult// reversing the order fixes the problemreturn{data: null,errors: [result.message]}}
π Actual behavior
In the noWork function, typescript could not narrow down the type to being HandledResult. When the or clause is switched, it works. Since Boolean(A || B) === Boolean(B || A) this seems like an error.
π Expected behavior
The ordering of the conditionals should not impact the narrowing of the type.
The text was updated successfully, but these errors were encountered:
This code is confusing to reason about because it is written in a way that presupposes an invariant violation occurring somewhere along the way.
Our logic effectively thinks of it this way:
In "noWork", it goes like this:
If "data" in result, then it's clearly a HandledResult -> OK
If not "data" in result, then it's clearly not a HandledResult, since that's a mandatory property, so it must be an UnhandledResult
If errors is present, then it's must be a UnhandledResult & { errors: unknown }; that's the only thing it can be. That's not a valid return type, so the program is correctly rejected
In "work", it goes like this:
If "errors" in result then it's clearly a HandledResult, since that's one of its listed optional properties
If "data" in result then it's clearly a HandledResult, since that's one of its listed required properties
Either way this code is fine
The functions don't make any sense if you assume the types are closed, which is the only operative condition in which using in makes any sense in the first place. I don't really see a way to change this without breaking more reasonable code that would expect not "data" in result to produce an UnhandledResult
It's interesting to me that in the "working" case, TS never considers the possibility that "data" in result might be false, like it does in the "non-working" case. It seems like it should, since || is commutative (short-circuiting of side effects notwithstanding).
thank you so much for your thoughtful explanation. π
it took a few re-reads, but it eventually makes sense why it works the way it does! I was treating them like open types where {data?: any, errors: any} could possibly exist & it prevented that.
Bug Report
π Search Terms
v4.9 conditionals unlisted property narrowing in operator #50666
π Version & Regression Information
v4.9+, including nightly
working in v4.8.4
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
In the
noWork
function, typescript could not narrow down the type to beingHandledResult
. When the or clause is switched, it works. SinceBoolean(A || B) === Boolean(B || A)
this seems like an error.π Expected behavior
The ordering of the conditionals should not impact the narrowing of the type.
The text was updated successfully, but these errors were encountered: