homomorphic mapped tuple doesn't preserve parallel tuple #46741
Copy link
Copy link
Closed
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
Bug Report
🔎 Search Terms
homomorphic mapped tuples, variadic tuple, parallel lists, zip tuples together, pipeline, compose function, keyof array.
🕗 Version & Regression Information
- This is the behaviour in every version of the playground from
4.0.5up to nightlyv4.6.0-dev2021-11-05- This is not applicable before
4.0.5since tuple spread[A,...V]doesn't exist that far back - and I reviewed the FAQ for entries about mapped types, I don't have primitives here.
- This is not applicable before
⏯ Playground Link and Explanation
Playground link with relevant code
💻 Code
interface A{a:any}
interface B{b:any}
interface C{c:any}
type Combine<X,Y> = {[K in keyof X]: [X[K], Y[K&keyof Y]]}
type XX = [A,B,C]
type YY = [1,2,3]
declare const example_value: [["x", "y"], [A, 1], [B, 2], [C, 3]]
let works_as_expected: Combine<['x',...XX], ['y',...YY]> = example_value
// let works_as_expected: [["x", "y"], [A, 1], [B, 2], [C, 3]]
type Alias<X extends any[],Y extends any[]> = Combine<['x',...X],['y',...Y]>
let doesnt_work: Alias<XX,YY> = example_value
// let doesnt_work: [["x", "y"], [A, "y"], [B, 1], [C, 2]]🙁 Actual behavior
works_as_expected is correctly the type [['x','y'], [A, 1], [B, 2], [C, 3]] but doesnt_work which is basically defined the exact same way except the spreading is done inside another type alias gives a totally different and incorrect type [["x", "y"], [A, "y"], [B, 1], [C, 2]]
🙂 Expected behavior
example should compile without errors, the type of doesnt_work should be the same as works_as_expected.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Activity
[-]mapped tuples together [/-][+]homomorphic mapped tuple doesn't preserve parallel tuple[/+]tadhgmister commentedon Nov 9, 2021
I came across this while trying to do variadic tuples with a
pipelinetype function, playgroundThat
pipelinefunction is correctly inferring generics in most cases I throw at it, if it wasn't for the correctly inferred generics producing a different and invalid type this would work great. (you can tell it is clearly invalid because the error says it isn't assignable to the type it was literally declared with) I think if this issue is fixed we may finally have the variadiccomposeandpipelinesignature that can infer some arguments of the in-between functions.ahejlsberg commentedon Jan 12, 2022
This one is interesting.
When instantiating a mapped type that is applied to a tuple type with variadic elements, we transform
M<[A, ...T, B]>into[...M<[A]>, ...M<T>, ...M<[B]>], and then rely on tuple type normalization to resolve the non-generic parts of the resulting tuple. So, say thatMis a mapped type that "boxes" every element:The instantiation
M<[number, ...T, string]>then turns into[{ value: number }, M<T>, { value: string }]. This is generally a desirable transformation because it ensures that a mapped type applied to a variadic tuple type is itself recognized as a tuple type, plus it reduces the genericity of the type and allows better checking of and inference from the non-generic parts. However, the transformation means that the iteration type variable (Kin example above) can really only be trusted to represent a property name in the type being iterated over (Tin example above), and cannot be trusted to always correlate to an index in some other tuple type or a final combined tuple type.Here's a simpler example:
Above, in the
T1example, the type[0, ...[0, 0, 0]]is normalized to[0, 0, 0, 0]beforeFoo<T>is applied to the tuple. But in theT2example, the tuple is split into two parts and the indices now shift.I can see how that is a bit surprising, but on the other hand, getting rid of the transformation has significant drawbacks. So, for now I'm going to say this is working as intended.
tadhgmister commentedon Jan 14, 2022
Intersting, thanks for the further information. Knowing this is how typescript is resolving the tuple spreading I can get the correct logic with this code which still doesn't quite give the auto-magical argument inference I was hoping but is functionally correct which is always good.
I suspect it still isn't working as I'd like because of the need to call
Tailwhich usesinferon the generic it is trying to resolve meaning to figure out whatVis you have to already know whatVis, I'm honestly really impressed with how well typescript manages the resolution already but if you can't just zip tuples together then I don't think this use case will ever work exactly as intended.Anyway thanks again for looking into this. Really appreciate typescript and your work to make it great! :)
typescript-bot commentedon Jan 16, 2022
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.