TypeScript: Omitting a property from tuple breaks destruction
Bug Report
🔎 Search Terms
tuple omit property
Searched in Bing and Github issues but I found nothing
🕗 Version & Regression Information
- This is the behavior in every version I tried
⏯ Playground Link
💻 Code
const foo = [1, "2"] as const
const [a, b] = foo // everything works well, a is number and b is string
const bar = [1, "2"] as Omit<[number, string], "at">
const [c, d] = bar // Oops! c and d are string | number now
🙁 Actual behavior
↑
c and d are both string | number
🙂 Expected behavior
a and b should have correct type
About this issue
- Original URL
- State: open
- Created a year ago
- Comments: 17 (6 by maintainers)
This
isArrayLikeType(parentType)check is at fault here: https://github.dev/microsoft/TypeScript/blob/fbd63e9e43dc4e64184b590fa156002a7be6d47e/src/compiler/checker.ts#L10376I’m not quite sure how this should be adjusted though. Currently
isArrayLikeTypeis defined like this:In general, tuples and arrays are not always handled in the compiler in various scenarios - one of other things that comes to my mind is that intersections with tuple/array members are not always handled as expected (although that works in this particular scenario). So perhaps a fix here should involve a more general overhaul of how those things are treated within the compiler instead of being an ad-hoc change to satisfy this particular scenario.
In user code, I’d recommend using a newer version of
Omitthat is homomorphic:But yeah the behavior describes seems a little off
Found it! The issue seems to happen when one of the tuple type members is assignable to the other. For instance:
Instead of
1,dwas typed asnumber, which I would assume TS is reducing fromnumber | 1. It’s easier to see when the elements are functions:And that kind of makes sense: here,
cis typed as accepting either anumberor specifically1; the latter type is just redundant. In fact, if you give them different return types so it’s not redundant, you actually get the right type and not a union.…And it’s at this point in the writeup that I discover the actual problem (sort of). All you need to do to “fix” this is to change your target to ES2021 or earlier. This problem only appears to occur while targeting ES2022 and ESNext. I have no idea if that’s intentional. My gut feeling is that it’s a bug, because it seems to behave in ways that can’t be right, but I suppose it’s possible TS is just expressing something that’s new in ES2022 that I’m not thinking of. (I’ve been surprised before…)
Here’s the issue reduced to something actually incorrect. Bizarrely, the type of the value changes depending on whether it’s called as a function: when it’s used in a function call, it picks of the union members, but it somehow picks the wrong one.
@so1ve That’s just the same thing I already pointed out above: declaration site shows
string | number, later uses have the proper type: PlaygroundHere’s a playground link for you.