Home > Software design >  keep track of a generic function type
keep track of a generic function type

Time:03-08

I'm implementing a typescript function that takes a mono-argument-function and outputs a slightly modified version, where the argument is wrapped in an object.
I can't figure out how to keep track of original function's generics:

    // ts v4.5.5

    type FnType<Arg, Ret> = (arg: Arg) => Ret
    
    type Wrapped<VFn extends FnType<any, any>> = (wrap: {
      _: VFn extends FnType<infer A, any> ? A : never
    }) => VFn extends FnType<any, infer B> ? B : never
    
    // implemented a function that takes a `FnT extends FnType` and outputs Wrapped<FnT>
    type Wrapper = <FnT extends FnType<any, any>>(v: FnT) => Wrapped<FnT>
    declare const wrapper: Wrapper
    
    // the returned `wrapped` function loses track of generic type T
    const wrapped1 = wrapper(<T>(t: T) => ({ t })) // const wrapped1: Wrapped<(<T>(t: T) => { t: T; })>
    const ret1 = wrapped1({ _: { a: 1 } }) // const ret1: { t: unknown; }
    
    
    type MyFnType = <T>(t: T) => { t: T }
    const myFnType:MyFnType = <T>(t: T) => ({ t })
    
    // even explicitly defining and passing FnType as generic and argument !
    
    const wrapped2 = wrapper<MyFnType>(<T>(t: T) => ({ t })) // const wrapped2: Wrapped<MyFnType>
    const ret2 = wrapped2({ _: { a: 1 } }) // const ret2: { t: unknown; }
    
    const wrapped3 = wrapper<MyFnType>(myFnType) // const wrapped3: Wrapped<MyFnType>
    const ret3 = wrapped2({ _: { a: 1 } }) // const ret3: { t: unknown; }

ts sandbox here

CodePudding user response:

Typescript will not preserve generics through functions extracting the parameters and the return type using a conditional type.

If you use as type parameters the args and the return type, typescript will preserve the generics in the resulting function:

type Wrapper = <A, R>(v: (a: A) => R) => (a: { _: A }) => R
declare const wrapper: Wrapper


const wrapped1 = wrapper(<T>(t: T) => ({ t }))
const ret1 = wrapped1({ _: { a: 1 } }) // const ret1: { t: { a: number }; }

Playground Link

  • Related