jeudi 28 février 2019

Typescript conditional types inferred by high order function

I have a function that can return a sync or async result

type HookHandler<T> = (context: MyClass<T>) => boolean | Promise<boolean>;

and a class that takes a list of that functions

class MyClass<T> {

    constructor(private handlers: Array<HookHandler<T>>) {

    }

    public invokeHandlers() : boolean | Promise<boolean> {
        // invoke each handler and return:
        // - Promise<boolean> if exist a handler that return a Promise<T>
        // - boolean if all handlers are synchronous
    }

}

I was wondering if there is any chance to make typescript infer the return type of the invokeHandlers() based on the given handlers. Consider that all the handlers are declared at design time:

const myClassSync = new MyClass<MyType>([
   (ctx) => true,
   (ctx) => false
]);

const myClassAsync = new MyClass<MyType>([
   async (ctx) => Promise.resolve(true),
   async (ctx) => Promise.reject()
]);

const myClassMix = new MyClass<MyType>([
   async (ctx) => Promise.resolve(true),
  (ctx) => true
]);

Can I make the return type of invokeHandlers() dependent of the types of the current given hanlders without an explicit casting? So for example

// all handlers are sync, infer boolean
const allHandlersAreOk: boolean = myClassSync.invokeHandlers()

// all handlers are async, infer Promise<boolean>
const allAsyncHandlersAreOk: Promise<boolean> = await myClassAsync.invokeHandlers()

// at least one handler is async, infer Promise<boolean>
const allMixedHandlersAreOk: Promise<boolean> = await myClassMix.invokeHandlers()

I can obviously return a simple Promise<boolean>, but I would loose the possibility to call the invokeHandlers() in synchronous contexts, and it want to avoid that.

Any suggestions or other design choice to face the problem? Thank you!

Aucun commentaire:

Enregistrer un commentaire