-
Notifications
You must be signed in to change notification settings - Fork 12.8k
homomorphic mapped tuple doesn't preserve parallel tuple #46741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I came across this while trying to do variadic tuples with a type Fn<P,R> = (arg:P)=>R
interface A{a:any} // interfaces for testing
interface B{b:any}
interface C{c:any}
interface D{d:any}
// Fns<[A,B,C], [1,2,3]> == [Fn<A,1>, Fn<B,2>, Fn<C,3>]
// these take a list of arguments and a list of return types and basically zips them together
type Fns_RetDriven<Args,Rets> = {[K in keyof Rets]: Fn<Args[K&keyof Args], Rets[K]>}
type Fns_ArgDriven<Args,Rets> = {[K in keyof Args]: Fn<Args[K], Rets[K&keyof Rets]>}
type Fns<Args,Rets> = Fns_RetDriven<Args,Rets>
& Fns_ArgDriven<Args,Rets>
// the real use case is a compose/pipeline function which seems to be infering generics properly but the type is just breaking
declare function pipeline<Init,V extends any[],Ret>(...fns: Fns<[Init,...V],[...V,Ret]>): Fn<Init,Ret>
declare const clearly_the_expected_type: Fns<[A,B,C], [B,C,D]>
pipeline(...clearly_the_expected_type)
//Err Argument of type '[Fn<A, B>, Fn<B, C>, Fn<C, D>]' is not
// assignable to parameter of type 'Fns<[A, B, C], [B, C, D]>'. That |
This one is interesting. When instantiating a mapped type that is applied to a tuple type with variadic elements, we transform type M<T> = { [K in keyof T]: { value: T[K] } }; The instantiation Here's a simpler example: type Foo<T> = { [K in keyof T]: K };
type Bar<A extends any[]> = Foo<[0, ...A]>; // [0, ...Foo<A>] because of transformation
type T0 = Foo<[0, 0, 0, 0]>; // ["0", "1", "2", "3"]
type T1 = Foo<[0, ...[0, 0, 0]]>; // ["0", "1", "2", "3"]
type T2 = Bar<[0, 0, 0]>; //["0", "0", "1", "2"] Above, in the 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. |
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 Anyway thanks again for looking into this. Really appreciate typescript and your work to make it great! :) type Tail<T extends unknown[]> = T extends [any, ...infer Rest] ? Rest : never;
type Fn<P,R> = (param:P)=>R;
/** Borrowed from SimplyTyped */
type Prev<T extends number> = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62][T];
type Fns_ArgDriven<Args extends unknown[], Rets extends unknown[]> = {[K in keyof Args]: Fn<Args[K], Rets[K&keyof Rets]>}
type Fns_RetDriven<Args extends unknown[], Rets extends unknown[]> = {[K in keyof Rets]: Fn<Args[K&keyof Args], Rets[K]>}
type Fns<Init, V extends unknown[], Ret> = V extends [] ? [Fn<Init, Ret>]
: [Fn<Init, V[0]>, ...Fns_ArgDriven<V, [...Tail<V>, Ret]>] & [...Fns_RetDriven<[Init,...V], V>, Fn<V[Prev<V["length"]>], Ret>]
declare function pipeline<Init, V extends unknown[], Ret>(...fns: Fns<Init, V, Ret>): Fn<Init, Ret>
// first test only type annotate the first one, 2nd gets argument infered, 3rd onward only gets unknown infered for parameter type
// as such final return is not captured correctly
const test1 = pipeline((a:number)=>a.toExponential(),
(b)=>b.toUpperCase(),
// ^?
(c)=>({foo:c}))
// ^?
// second test, annotate first and third, now 2nd is set to unknown
// gives type error that methods can't be used on unknown but return is correct
const test2 = pipeline((a:number)=>a.toExponential(),
(b)=>b.toUpperCase(),
// ^?
(c: string)=>({foo:c}))
// ^?
declare const should_very_much_work: [Fn<1,2>, Fn<2,3>, Fn<3,4>]
const yay_it_works = pipeline(...should_very_much_work)
// ^? |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Bug Report
🔎 Search Terms
homomorphic mapped tuples, variadic tuple, parallel lists, zip tuples together, pipeline, compose function, keyof array.
🕗 Version & Regression Information
4.0.5
up to nightlyv4.6.0-dev2021-11-05
4.0.5
since tuple spread[A,...V]
doesn't exist that far back⏯ Playground Link and Explanation
Playground link with relevant code
💻 Code
🙁 Actual behavior
works_as_expected
is correctly the type[['x','y'], [A, 1], [B, 2], [C, 3]]
butdoesnt_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 asworks_as_expected
.The text was updated successfully, but these errors were encountered: