0
votes

Let's say I define a type CoolType like this:

type CoolType<T> = {message: string, result: T} 

Then I define a CoolFunction type to describe a function that returns CoolType:

type CoolFunction = <T>() => CoolType<T>

CoolFunction is the type of parameter that a second function expects:

function superCoolFunction(coolFunction: CoolFunction) {
    return coolFunction()
}

Eventually, after all these definitions, I try to run some code like this:

const res = superCoolFunction(<string>() => {
    return {message: 'I am the message', result: 'I am the result'}
})

but, on <string>() => { of the above code I get an error from the compiler telling me that

'string' is declared but its value is never read.ts(6133) Argument of type '() => { message: string; result: string; }' is not assignable to parameter of type 'CoolFunction'. Call signature return types '{ message: string; result: string; }' and 'CoolType' are incompatible. The types of 'result' are incompatible between these types. Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'.ts(2345)

Any idea what I am doing wrong? Here's a stackblitz that reproduces the error.

1

1 Answers

3
votes

You seem to have generics in the wrong places. I think what you want is:

type CoolFunction<T> = () => CoolType<T>;

i.e. CoolFunctions also take a generic type. Then your higher-order function can propagate the generic:

function superCoolFunction<T>(coolFunction: CoolFunction<T>): CoolType<T> {
    return coolFunction();
}

Now the specific type can be inferred by the compiler:

superCoolFunction(() => ({ message: 'foo', result: 123 }));
// function superCoolFunction<number>(coolFunction: CoolFunction<number>): CoolType<number>

or supplied explicitly:

superCoolFunction<string>(() => ({ message: 'foo', result: 'bar' }));