0

The following code is running normally.

function test<T extends object, K extends keyof T, P extends (v: T[K]) => any>(
    item: T,
    key: K,
    transform: P
): ReturnType<P> {
    return transform(item[key])
}

but when I set a default function to param transform it errors

function test<T extends object, K extends keyof T, P extends (v: T[K]) => any>(
    item: T,
    key: K,
    transform: P = v => v
): ReturnType<P> {
    return transform(item[key])
}

Type '(v: T[K]) => T[K]' is not assignable to type 'P'. '(v: T[K]) => T[K]' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint '(v: T[K]) => any'.

how can I modify this code to run it normally with default method v => v

  • 1
    Why have the generic type be the whole function, rather than its return value: `function test(item: T, key: K, transform: (T[K]) => R = v => v): R`. – jonrsharpe Dec 26 '20 at 09:16
  • At least [one of these](/search?q=%5Btypescript%5D+could+be+instantiated+with+a+different+subtype+of+constraint) should answer your question. Basically, it's what the error says: It would be possible to call `test` such that `v => v` is not a valid implementation of `transform`. You'd have to make the types of `transform`'s `v` parameter and its return type the same. – T.J. Crowder Dec 26 '20 at 09:16
  • @jonrsharpe I tried with `function test(item: T,key: K,transform: (v: T[K]) => R = v => v): R ` and the error is `Type 'T[K]' is not assignable to type 'R'` Typescript tried to make T[K] = R – Love_of_red_moon Dec 26 '20 at 12:47

1 Answers1

0

It's not necessary to dare to use type-level computation. It's enough to write concrete types.

type F<X, Y> = (x: X) => Y 

function test<T extends object, K extends keyof T>(
    item: T,
    key: K,
    transform: F<T[K],any> = v => v
): ReturnType<F<T[K],any>> {
      return transform(item[key])
}

Inference of TypeScript is not so wise acrually. In this case, compiler can't infer well type variable P. In token of that, following code is inferred correctly:

function test2(_: ((x:any) => number) extends ((x:any) => any) ? number : string = 1){
}

There is keyword infer to only infer type variables all the way (and this can use ONLY after extends in typing of parameters).

Type-level computation in TypeScript is developing yet, let's look forward to it.

Akihito KIRISAKI
  • 1,243
  • 6
  • 12