Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

TypeScript cannot infer type arguments correctly when swapping function parameters. #57287

Closed
@webvv

Description

@webvv

🔎 Search Terms

It's been 2 years that I'm playing with higher order functions and kinds. This is part of my effort on creating a course of "Functional Programming with TypeScript". You can find the contenthere.
I'm working on a next video to teach "Functor composition", but I'm getting to a strange behavior on argument inference that I cannot reason about. You have an example of what I'm working on (compose function) in thedocs here.
What baffles me is why swapping place of arguments in the compose function, makes my examples for higher kinded types fail. I've explained this completely in a question with a sample snippet of code and playground inStackOverFlow here. I'm trying this with the latest version of TypeScript.

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about type inference.

⏯ Playground Link

Link

💻 Code

Unfortunately this is the smallest snippet of code I could shrunk the issue to:

typeOption<A>=Some<A>|NoneinterfaceSome<A>{_tag:'Some'value:A}interfaceNone{_tag:'None'}constsome=<A,>(x:A):Option<A>=>({_tag:'Some',value:x})constnone:Option<never>={_tag:'None'}constisNone=<A,>(x:Option<A>):x isNone=>x._tag==='None'// --------------------typeEither<E,A>=Left<E>|Right<A>interfaceLeft<E>{_tag:'Left'left:E}interfaceRight<A>{_tag:'Right'right:A}constleft=<E,A=never>(x:E):Either<E,A>=>({_tag:'Left',left:x})constright=<A,E=never>(x:A):Either<E,A>=>({_tag:'Right',right:x})constisLeft=<E,A>(a:Either<E,A>):a isLeft<E>=>a._tag==='Left'// --------------------interfaceURItoKind1<A>{'Option':Option<A>}interfaceURItoKind2<E,A>{'Either':Either<E,A>}typeURIS1=keyofURItoKind1<any>typeURIS2=keyofURItoKind2<any,any>typeKind1<URIextendsURIS1,A>=URItoKind1<A>[URI]typeKind2<URIextendsURIS2,E,A>=URItoKind2<E,A>[URI]typeHKT1<URI,A>={URI:URI;a:A};typeHKT2<URI,A,B>={URI:URI;a:A;b:B}interfaceFunctor1<FextendsURIS1>{readonlyURI:Fmap:<A,B>(f:(a:A)=>B)=>(fa:Kind1<F,A>)=>Kind1<F,B>}interfaceFunctor2<FextendsURIS2>{readonlyURI:Fmap:<E,A,B>(f:(a:A)=>B)=>(fa:Kind2<F,E,A>)=>Kind2<F,E,B>}interfaceFunctor<F>{readonlyURI:Fmap:<A,B>(f:(a:A)=>B)=>(fa:HKT1<F,A>)=>HKT1<F,B>}// --------------------------constoptionFunctor:Functor1<'Option'>={URI:'Option',map:<A,B>(f:(x:A)=>B)=>(fa:Option<A>):Option<B>=>isNone(fa) ?none :some(f(fa.value))}consteitherFunctor:Functor2<'Either'>={URI:'Either',map:<E,A,B>(f:(x:A)=>B)=>(fa:Either<E,A>):Either<E,B>=>isLeft(fa) ?fa :right(f(fa.right))}// ---------------------------typeCompose=<A,B,C>(f:(x:B)=>C,g:(x:A)=>B)=>(x:A)=>Cconstcompose:Compose=(f,g)=>x=>f(g(x))typeComposeR=<A,B,C>(g:(x:A)=>B,f:(x:B)=>C)=>(x:A)=>CconstcomposeR:ComposeR=(g,f)=>x=>f(g(x))// ---------------------------typeIncrement=(x:number)=>numberconstincrement:Increment=x=>x+1typeToStringg=(x:number)=>stringconsttoStringg:ToStringg=x=>`${x}`constcomposed=compose(toStringg,increment)constcomposedR=composeR(increment,toStringg)composed(12)// "13"composedR(12)// "13"// This section compiles ok and types inferred correctly when composing functions.// ---------------------------constmap1=optionFunctor.mapconstmap2=eitherFunctor.mapconstcomposed1=compose(map1,map2)// <=== map2 has error and types cannot be inferred correctlyconstcomposed2=composeR(map1,map2)// <=== map2 is ok here!// Try switching map1 and map2. Why in `composed1` TypeScript cannot infer types correctly? how can I fix it?

🙁 Actual behavior

Type inference on compose function is not working as expected. This is from the snippet and sample code above.

// ...constcomposed1=compose(map1,map2)// <=== map2 has error and types cannot be inferred correctlyconstcomposed2=composeR(map1,map2)// <=== map2 is ok here!

🙂 Expected behavior

In both ways of composingmap functions (map1,map2), typescript could infer generic type arguments correctly.

Additional information about the issue

This code is heavily influenced byfp-ts library. (I've already investigated the library. The library has aflow function that is implemented similar to thecompose function in theTypeScript docs here, but there are no function like mycompose function there that parameters are swapped.
The way I implemented thecompose function and choosing the arguments order is based on the Composition-Operator in Math and Haskell for when we try to compose functions: (f o g in Math andf . g in Haskell means callg with the input parameter first and then callf with the output ofg, and return)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp