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
Destructured union of tuples with non-uniform length results in diagnostic when narrowing #49626
declareconst[a,b,c]: ["a",number]|["b",number,boolean];if(a==="a"){// If this line is commented, the diagnostic is not thrownc;}else{c;}
🙁 Actual behavior
When c is accessed in the branch for ["a", number], it throws a diagnostic on the original declaration for c since the control flow analysis tries to access narrowedType[2] which does not exist on ["a", number]
🙂 Expected behavior
There should be no diagnostic thrown and c should simply be undefined (like it already is) or the diagnostic should be thrown where c is accessed rather than defined.
I don't see how either proposed behavior is necessarily better than the current one. It's very weird to get an error about "no element at position 2" on a bare identifier reference -- if you were many lines down in the function, that would seem like a bug, or possibly extremely misleading if you wrote something like arr[c].
I don't see how either proposed behavior is necessarily better than the current one. It's very weird to get an error about "no element at position 2" on a bare identifier reference -- if you were many lines down in the function, that would seem like a bug, or possibly extremely misleading if you wrote something like arr[c].
That is a good point but I think the existing behavior is bad as it implies the destructuring is incorrect whereas indexing a possible member is valid everywhere else in TypeScript (including destructuring). I think not throwing a diagnostic would be more consistent and less confusing if someone does encounter this even if c's value isn't particularly useful.
Yeah, I get that none of the alternatives is necessarily ideal but having a code change somewhere produce an error in an earlier declaration that is possibly several screens away seems like the worst possible behavior. A valid declaration suddenly becoming invalid based on how later code uses it is something I’m pretty sure never happens any other time in TS.
The situation shown with tuples is analogous to the situation with properties where you also get an error with this code:
declare const { x, y, z }: { x:1, y: 1 } | { x:2, y:2, z:2 };
The errors in both cases are correct. The third element or third property produces an error because the type as written does not really have a third element or property, it only has the potential of one.
It sounds like you want to "collapse the wave function" and unify your provided type ["a", number] | ["b", number, boolean] to the conceptually similar, but actually distinct, type ["a" | "b", number, boolean | undefined].
Unification is not an operation for which TS provides a helper, but you can implement it in a generic way so that you can unify any tuple. A working example is shown at this playground:
Callionica (@callionica) Right, but the issue here is that c is only an error conditionally, if the code later tries to use it in the wrong branch. That’s really confusing, especially if the code that actually triggers the error isn’t on the same screen as the declaration where the error is being flagged.
Yes, that is different behaviour than the object destructuring example: declare const { x, y, z }: { x:1, y: 1 } | { x:2, y:2, z:2 };
which triggers the compiler error pointing at z with no need to access z elsewhere.
There should be similar behaviour in both cases.
Tuples can't have excess members. This is not the case with objects, so it can't reasonably conclude that z is undefined but in the tuple case it is safe to.
Activity
RyanCavanaugh commentedon Jun 22, 2022
I don't see how either proposed behavior is necessarily better than the current one. It's very weird to get an error about "no element at position 2" on a bare identifier reference -- if you were many lines down in the function, that would seem like a bug, or possibly extremely misleading if you wrote something like
arr[c].Fireboltofdeath commentedon Jun 23, 2022
That is a good point but I think the existing behavior is bad as it implies the destructuring is incorrect whereas indexing a possible member is valid everywhere else in TypeScript (including destructuring). I think not throwing a diagnostic would be more consistent and less confusing if someone does encounter this even if
c's value isn't particularly useful.fatcerberus commentedon Jun 23, 2022
Yeah, I get that none of the alternatives is necessarily ideal but having a code change somewhere produce an error in an earlier declaration that is possibly several screens away seems like the worst possible behavior. A valid declaration suddenly becoming invalid based on how later code uses it is something I’m pretty sure never happens any other time in TS.
RyanCavanaugh commentedon Jun 23, 2022
If someone wants to put up a PR to make
ccorrectlyundefinedthat'd probably be worth evaluatingcallionica commentedon Jun 23, 2022
The situation shown with tuples is analogous to the situation with properties where you also get an error with this code:
The errors in both cases are correct. The third element or third property produces an error because the type as written does not really have a third element or property, it only has the potential of one.
It sounds like you want to "collapse the wave function" and unify your provided type
["a", number] | ["b", number, boolean]to the conceptually similar, but actually distinct, type["a" | "b", number, boolean | undefined].Unification is not an operation for which TS provides a helper, but you can implement it in a generic way so that you can unify any tuple. A working example is shown at this playground:
Playground
fatcerberus commentedon Jun 23, 2022
Callionica (@callionica) Right, but the issue here is that
cis only an error conditionally, if the code later tries to use it in the wrong branch. That’s really confusing, especially if the code that actually triggers the error isn’t on the same screen as the declaration where the error is being flagged.callionica commentedon Jun 23, 2022
Yes, that is different behaviour than the object destructuring example:
declare const { x, y, z }: { x:1, y: 1 } | { x:2, y:2, z:2 };which triggers the compiler error pointing at
zwith no need to accesszelsewhere.There should be similar behaviour in both cases.
Fireboltofdeath commentedon Jun 24, 2022
Tuples can't have excess members. This is not the case with objects, so it can't reasonably conclude that
zisundefinedbut in the tuple case it is safe to.playground