1

Suppose the following:

type GenericFunc<T> = () => Promise<T>

interface FuncWithModifier<T> extends GenericFunc<T> {
  modifier: GenericFunc<T>
}

const wrapFunction = <T> (func: GenericFunc<T>): FuncWithModifier<T> => {
    const modifier: GenericFunc<T> = async () => func()
    return Object.assign(func, { modifier })
}

Now I can create functions with a modifier, like this:

const stringFunc: FuncWithModifier<string> = wrapFunction(async () => 'foo')
const str1: string = await stringFunc()
const str2: string = await stringFunc.modifier()

It also works without an explicit type annotation:

const implicitStringFunc = wrapFunction(async () => 'foo')
const str3: string = await implicitStringFunc()
const str4: string = await implicitStringFunc.modifier()

What I want now, is a generic function, e.g. something like:

// doesn't work, invalid syntax!
const genericFunc = <T = unknown> wrapFunction(async () => null as any)

… so that I can use it like this:

const unknown1: unknown = await genericFunc()
const unknown2: unknown = await genericFunc.modifier()
const num1: number = await genericFunc<number>()
const num2: number = await genericFunc.modifier<number>()
const bool1: boolean = await genericFunc<boolean>()
const bool2: boolean = await genericFunc.modifier<boolean>()

However, it seems that it is not possible to keep the type parameter from the called function (wrapFunction above) and instead apply it to the result. Is there another way that I could achieve this?

TS Playground

3
  • You are able to infer return type from wrapFunction argument. Not sure why do you need extra generic. However, if you need, you can try this, but it works only with any. I just not sure whether you want to use any or not Commented Jan 12, 2023 at 11:09
  • Thanks for your comment and answer. The wrapped function should be generic such that it's return value defaults to unknown if no type parameter is given. But it should be allowed to override the return value by giving a type parameter. That should work the same for the wrapped function itself and its modifier function. Commented Jan 27, 2023 at 10:11
  • I've clarified my question. Commented Jan 27, 2023 at 10:18

1 Answer 1

1

Consider this example:


type FuncWithModifier<T> = {
    <R = 'unset'>(): R extends 'unset' ? Promise<T> : Promise<R>
    modifier: <R = 'unset'>() => R extends 'unset' ? Promise<T> : Promise<R>
}

function wrapFunction<R,>(func: () => Promise<R>): FuncWithModifier<R>
function wrapFunction<R,>(func: () => Promise<R>) {
    const modifier = async () => func()
    return Object.assign(func, { modifier })
}


const stringFunc: FuncWithModifier<string> = wrapFunction(async () => 'foo')
const str1 = await stringFunc()
const str2 = await stringFunc.modifier()

const implicitStringFunc = wrapFunction(async () => 2)
const str3 = await implicitStringFunc()
const str4 = await implicitStringFunc.modifier()



const genericFunc = wrapFunction(async () => 42)
const num1 = await genericFunc<number>()
const num2 = await genericFunc.modifier<number>()
const bool1 = await genericFunc<boolean>()
const bool2 = await genericFunc.modifier<boolean>()

Playground

I have used unset default value to check whether generic was provided or not.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.