Kirill Reznikov
Kirill Reznikov

Reputation: 2357

Typescript limitation workaround

I have a code, that is totaly OK from the human perspective. But it looks like typescript type system have hard time to understand it. Is there a smart way to hint compiler that everything is fine in that line?

  const isMustToRun: boolean = isFunc(condition) ? condition() : condition;

code:

    export const noop = function() {
    };

    export const isFunc = function(obj: any): boolean {
      return typeof obj === 'function';
    };

    /**
     *
     * @param func funtion to run
     * @param condition condition check
     * @param args
     */
    export const runIf = function f(condition: (Function | boolean), func: Function, ...args: any[]) {
      return () => {
        const isMustToRun: boolean = isFunc(condition) ? condition() : condition;
        return isMustToRun ? func(...args) : noop();
      };
    };

If I wrote

typeof condition === 'function'

instead of "isFunc" call, then it works. But I don't want to repeat the code..

Upvotes: 3

Views: 216

Answers (2)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250106

You need to transform isFunc in a custom type guard and your code will work as expected.:

export const isFunc = function(obj: any): obj is Function {
  return typeof obj === 'function';
};

I would recommend not using Function though as it is not really very type safe, you can tighten up the function type with a function signature, and use the Extract conditional type to preserve the actual type of the passed in function to the isFunc type-guard:

export const isFunc = function<T>(obj: T): obj is Extract<T, Function> {
    return typeof obj === 'function';
};

export const runIf = function f(condition: ((()=> boolean) | boolean), func: Function, ...args: any[]) {
    return () => {
        const isMustToRun: boolean = isFunc(condition) ? condition() : condition;
        return isMustToRun ? func(...args) : noop();
     };
};

Or a fully type safe version of runIf that checks the args agains the func parameters:

export const runIf = function f<T extends (...a: any)=>any>(condition: ((()=> boolean) | boolean), func: T, ...args: Parameters<T>): (()=>ReturnType<T>) {
    return () => {
    const isMustToRun: boolean = isFunc(condition) ? condition() : condition;
        return isMustToRun ? func(...args as any[]) : noop();
    };
};

function testFn(n: string) : number { return +n;}
runIf(true, testFn, "0"); //ok
runIf(true, testFn, 0); //err

Upvotes: 2

basarat
basarat

Reputation: 276115

Change the return type of

export const isFunc = function(obj: any): boolean {
  return typeof obj === 'function';
};

to be is Function

export const isFunc = function(obj: any): obj is Function {
  return typeof obj === 'function';
};

More

This is called a user defined type guard 🌹

Upvotes: 4

Related Questions