The reason for this becomes more clear if you look at the type signature of fnGeneric as it would appear in a .d.ts file.
declare const fnGeneric: <V,I>(fn:<U>(param:U) => V, param:I) => V
Does not relate U and I in any way. Here is what the type checker can infer from this signature:
fnGeneric is a function defined for all types V, and I, receiving 2 parameters fn and param and returning the type V
fn is a function defined for all types U receiving 1 parameter param, of type U and returning V
param is of type I
This type-checks fine, because fn is a function that can turn any type into a V, so passing it an I in the definition fn(param) is perfectly acceptable.
The problems start when you try to call this function. In order to call fnGeneric, you need to give is a function that can take any type, and turn it into the type you want to get out of fnGeneric. This is impossible! Passing in a const fn: (some: string) => string like in your example doesn't work, becase fn doesn't accept any type, it only accepts strings. The signature would need to be const fn: <U>(some: U) => string. Hence the error:
Argument of type '(some: string) => string' is not assignable to parameter of type '(param: U) => string'. Types of parameters 'some' and 'param' are incompatible. Type 'U' is not assignable to type 'string
Additionally, based on your example fnGeneric(fn, 5), you are trying to pass a number to a function that takes a string by coercing fn to be a function that takes any type of argument. Here is a code snippet that type checks:
const fnGeneric = <A>(fn: <B>(param: B) => B, param: A): A => fn(param);
const fn = <A>(some: A): A => some;
const result: number = fnGeneric(fn, 5); // result = 5
fn in this example is commonly known as the identity function - which returns the parameter it is given, and it is the only valid implementaion (without arbitrary type casting) of a function with the signature <A>(a: A) => A. By extension, fnGeneric is a function that takes the identity function and a parameter of any type, and returns the application of the identity function to that parameter.
What is wrong with this pattern?
fnGeneric as defined doesn't make any sense, it would be impossible to write a program that type checks that satisfies the signature. The only way to make the types work simply makes it redundant. There aren't any cases where it would be preferable to call fnGeneric(identity, x) instead of just identity(x) (or for that matter, simply the expression x). They are all completely equivalent.
UVandIdefined? - Titian Cernicova-Dragomirconst fnGeneric = <V,I>(fn:<U>(param:U) => V, param:I) => fn(param);sorry editor somehwow remove it - Ondrej Basistaconst fnGeneric = <V,I, U>(fn:(param:U) => V, param:U) => fn(param); const fn = (some:string) => some; const result = fnGeneric(fn,"5");?fnshould not be itself a generic function .. - Titian Cernicova-Dragomir