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
Incorrent return type inference when using infer with generic function #48820
You can create such a type definition, but the type information of the order of the constant array will be lost when you use Array.prototype.map(), so you will have to cast it with as in the end.
harry (@harry0000) Could you explain why we need Higher Kinded Types in this case?
Why it's not possible to infer correct type?
typeApplyFn<Fnextends(item: any)=>any,T>=Fnextends(item: T)=> infer R ? R : nevertypeFn=<T>(item: T)=>{foo: T}typeY=ApplyFn<Fn,string>
The compiler knows that Fn type is <T>(item: T) => {foo: T} and when it extends (item: T) => infer R it could substitute string into it and get a correct type (item: string) => {foo: string}
The compiler knows that Fn type is <T>(item: T) => {foo: T}
This definition of Fn is the definition of a function that takes any type T as an argument without any restrictions by extends. Therefore, it must be guaranteed to execute no matter what type is passed as an argument based on its own constraints (definition). T is inferred to be unknown within the type definition, but this is unknown because we do not know what is actually passed to the function. Also, it actually can take any types to the function argument.
declareconstf: Fn;f();// Expected 1 arguments, but got 0.(2554)// const f: <unknown>(item: unknown) => {// foo: unknown;// }typeP=Parameters<Fn>// type P = [item: unknown]typeR=ReturnType<Fn>// type R = { foo: unknown; }
What you are actually trying to do is pass string to T in Fn, but writing Fn extends (item: string) => infer R does not pass string to T in Fn, so infer R returns { foo: unknown; }.
typeFn=<T>(item: T)=>{foo: T}// Since Fn can be executed even if a string type is passed, this "inheritance relationship" is valid.typeY=Fnextends(item: string)=> infer R ? R : never;// type Y = { foo: unknown; }
Since the inheritance relation check only checks whether the inheritance relation is satisfied, in order to determine T in Fn, a type parameter must be passed to Fn, which is not possible because there are no Higher Kinded Types.
typeFn<T>=(item: T)=>{foo: T}
Edit:
Why did you mistakenly think that you could pass any type to the type parameter of a generic function by checking the inheritance relationship with extends? From your experience with other programming languages? (just my interesting)
Why did you mistakenly think that you could pass any type to the type parameter of a generic function by checking the inheritance relationship with extends? From your experience with other programming languages? (just my interesting)
I thought type Fn<T> = (item: T) => {foo: T} is the same as type Fn = <T>(item: T) => {foo: T} but I'm not sure why and I am still not sure why it is not.
Do you know by any chance if it is possible to write tupleMap with HKT emulation?
Unfortunately, no.
If you use fp-ts, you can simply write the following, but as I mentioned before, the type information of the tuple order will be lost, so you will have to cast it with as in the end.
Maybe I should have written this workaround from the beginning? Generic functions delay determining the type until the argument is passed, and currying can further delay that determination.
constmap: <A,B>(f: (a: A)=>B)=>(fa: ReadonlyArray<A>)=>ReadonlyArray<B>=(f)=>{return(fa)=>fa.map(f);}constitems=map(itemMapper)(tuple);// const items: readonly {item: "foo" | "bar" | "buz"}[]constm=map(itemMapper);// const m: <T>(fa: readonly T[]) => readonly { item: T; }[]typeP=Parameters<typeofm>// type P = [fa: readonly unknown[]]typeR=ReturnType<typeofm>// type R = readonly { item: unknown; }[]
However, if no arguments have been passed yet (the type has not yet been determined: including references in the type definition), it will be unknown (See also f(), type P, and type R in the previous answer.)
Activity
craigphicks commentedon Apr 23, 2022
Probably not the level of generality you were hoping for but -
user753 commentedon Apr 24, 2022
Craig P Hicks (@craigphicks) thanks. But I don't think it can help me. I want to create map function for tuples
harry0000 commentedon Apr 24, 2022
user753
playground
user753 commentedon Apr 24, 2022
harry (@harry0000) thanks, but I want it to work like this
harry0000 commentedon Apr 25, 2022
user753 I recently answered the exact same question on Reddit, and my conclusion is no different than the answer to that question.
https://www.reddit.com/r/typescript/comments/u7qcdv/is_it_possible_to_map_an_array_literal_to_another/
playground
And TS does not have Higher Kinded Types, you cannot write as below:
#1213
#44875
user753 commentedon Apr 25, 2022
harry (@harry0000) Could you explain why we need Higher Kinded Types in this case?
Why it's not possible to infer correct type?
The compiler knows that
Fntype is<T>(item: T) => {foo: T}and when itextends (item: T) => infer Rit could substitute string into it and get a correct type(item: string) => {foo: string}harry0000 commentedon Apr 25, 2022
This definition of
Fnis the definition of a function that takes any typeTas an argument without any restrictions byextends. Therefore, it must be guaranteed to execute no matter what type is passed as an argument based on its own constraints (definition).Tis inferred to beunknownwithin the type definition, but this is unknown because we do not know what is actually passed to the function. Also, it actually can take any types to the function argument.https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-constraints
What you are actually trying to do is pass
stringtoTinFn, but writingFn extends (item: string) => infer Rdoes not passstringtoTinFn, soinfer Rreturns{ foo: unknown; }.Since the inheritance relation check only checks whether the inheritance relation is satisfied, in order to determine
TinFn, a type parameter must be passed toFn, which is not possible because there are no Higher Kinded Types.Edit:
Why did you mistakenly think that you could pass any type to the type parameter of a generic function by checking the inheritance relationship with
extends? From your experience with other programming languages? (just my interesting)user753 commentedon Apr 25, 2022
harry (@harry0000) Thanks. Now I get it. The
<T>was on the wrong side of the type and it should beAnd it is possible in some way to solve
ApplyFnproblemDo you know by any chance if it is possible to write
tupleMapwith HKT emulation? Likehttps://github.com/gcanti/fp-ts/blob/master/src/HKT.ts
I thought
type Fn<T> = (item: T) => {foo: T}is the same astype Fn = <T>(item: T) => {foo: T}but I'm not sure why and I am still not sure why it is not.harry0000 commentedon Apr 25, 2022
user753
Unfortunately, no.
If you use fp-ts, you can simply write the following, but as I mentioned before, the type information of the tuple order will be lost, so you will have to cast it with
asin the end.playground
Edit:
Maybe I should have written this workaround from the beginning? Generic functions delay determining the type until the argument is passed, and currying can further delay that determination.
playground
However, if no arguments have been passed yet (the type has not yet been determined: including references in the type definition), it will be
unknown(See alsof(),type P, andtype Rin the previous answer.)