Typescript: How to define type for a function callback (as any function type, not universal any) used in a method parameter

前端 未结 8 1003
刺人心
刺人心 2020-11-30 17:19

Currently I have type definition as:

interface Param {
    title: string;
    callback: any;
}

I need something like:

inter         


        
相关标签:
8条回答
  • 2020-11-30 17:23

    There are four abstract function types, you can use them separately when you know your function will take an argument(s) or not, will return a data or not.

    export declare type fEmptyVoid = () => void;
    export declare type fEmptyReturn = () => any;
    export declare type fArgVoid = (...args: any[]) => void;
    export declare type fArgReturn = (...args: any[]) => any;
    

    like this:

    public isValid: fEmptyReturn = (): boolean => true;
    public setStatus: fArgVoid = (status: boolean): void => this.status = status;
    

    For use only one type as any function type we can combine all abstract types together, like this:

    export declare type fFunction = fEmptyVoid | fEmptyReturn | fArgVoid | fArgReturn;
    

    then use it like:

    public isValid: fFunction = (): boolean => true;
    public setStatus: fFunction = (status: boolean): void => this.status = status;
    

    In the example above everything is correct. But the usage example in bellow is not correct from the point of view of most code editors.

    // you can call this function with any type of function as argument
    public callArgument(callback: fFunction) {
    
        // but you will get editor error if call callback argument like this
        callback();
    }
    

    Correct call for editors is like this:

    public callArgument(callback: fFunction) {
    
        // pay attention in this part, for fix editor(s) error
        (callback as fFunction)();
    }
    
    0 讨论(0)
  • 2020-11-30 17:24

    Typescript: How to define type for a function callback used in a method parameter?

    You can declare the callback as 1) function property or 2) method:

    interface ParamFnProp {
        callback: (a: Animal) => void; // function property
    }
    
    interface ParamMethod {
        callback(a: Animal): void; // method
    }
    

    There is an important typing difference since TS 2.6:

    You get stronger ("sound") types in --strict or --strictFunctionTypes mode, when a function property is declared. Let's take an example:

    const animalCallback = (a: Animal): void => { } // Animal is the base type for Dog
    const dogCallback = (d: Dog): void => { } 
    
    // function property variant
    const param11: ParamFnProp = { callback: dogCallback } // error: not assignable
    const param12: ParamFnProp = { callback: animalCallback } // works
    
    // method variant
    const param2: ParamMethod = { callback: dogCallback } // now it works again ...
    

    Technically spoken, methods are bivariant and function properties contravariant in their arguments under strictFunctionTypes. Methods are still checked more permissively (even if not sound) to be a bit more practical in combination with built-in types like Array.

    Summary

    • There is a type difference between function property and method declaration
    • Choose a function property for stronger types, if possible

    Playground sample code

    0 讨论(0)
  • 2020-11-30 17:25

    Typescript from v1.4 has the type keyword which declares a type alias (analogous to a typedef in C/C++). You can declare your callback type thus:

    type CallbackFunction = () => void;
    

    which declares a function that takes no arguments and returns nothing. A function that takes zero or more arguments of any type and returns nothing would be:

    type CallbackFunctionVariadic = (...args: any[]) => void;
    

    Then you can say, for example,

    let callback: CallbackFunctionVariadic = function(...args: any[]) {
      // do some stuff
    };
    

    If you want a function that takes an arbitrary number of arguments and returns anything (including void):

    type CallbackFunctionVariadicAnyReturn = (...args: any[]) => any;
    

    You can specify some mandatory arguments and then a set of additional arguments (say a string, a number and then a set of extra args) thus:

    type CallbackFunctionSomeVariadic =
      (arg1: string, arg2: number, ...args: any[]) => void;
    

    This can be useful for things like EventEmitter handlers.

    Functions can be typed as strongly as you like in this fashion, although you can get carried away and run into combinatoric problems if you try to nail everything down with a type alias.

    0 讨论(0)
  • 2020-11-30 17:25

    Hopefully, this will help...

    interface Param {
        title: string;
        callback: (error: Error, data: string) => void;
    }
    
    

    Or in a Function

    
    let myfunction = (title: string, callback: (error: Error, data: string) => void): string => {
    
        callback(new Error(`Error Message Here.`), "This is callback data.");
        return title;
    
    }
    
    
    0 讨论(0)
  • 2020-11-30 17:26

    You can define a function type in interface in various ways,

    1. general way:
    export interface IParam {
      title: string;
      callback(arg1: number, arg2: number): number;
    }
    
    1. If you would like to use property syntax then,
    export interface IParam {
      title: string;
      callback: (arg1: number, arg2: number) => number;
    }
    
    1. If you declare the function type first then,
    type MyFnType = (arg1: number, arg2: number) => number;
    
    export interface IParam {
      title: string;
      callback: MyFnType;
    }
    

    Using is very straight forward,

    function callingFn(paramInfo: IParam):number {
        let needToCall = true;
        let result = 0;
       if(needToCall){
         result = paramInfo.callback(1,2);
        }
    
        return result;
    }
    
    1. You can declare a function type literal also , which mean a function can accept another function as it's parameter. parameterize function can be called as callback also.
    export interface IParam{
      title: string;
      callback(lateCallFn?:
                 (arg1:number,arg2:number)=>number):number;
    
    }
    
    0 讨论(0)
  • 2020-11-30 17:38

    Here's an example of a function that accepts a callback

    const sqk = (x: number, callback: ((_: number) => number)): number => {
      // callback will receive a number and expected to return a number
      return callback (x * x);
    }
    
    // here our callback will receive a number
    sqk(5, function(x) {
      console.log(x); // 25
      return x;       // we must return a number here
    });
    

    If you don't care about the return values of callbacks (most people don't know how to utilize them in any effective way), you can use void

    const sqk = (x: number, callback: ((_: number) => void)): void => {
      // callback will receive a number, we don't care what it returns
      callback (x * x);
    }
    
    // here our callback will receive a number
    sqk(5, function(x) {
      console.log(x); // 25
      // void
    });
    

    Note, the signature I used for the callback parameter ...

    const sqk = (x: number, callback: ((_: number) => number)): number

    I would say this is a TypeScript deficiency because we are expected to provide a name for the callback parameters. In this case I used _ because it's not usable inside the sqk function.

    However, if you do this

    // danger!! don't do this
    const sqk = (x: number, callback: ((number) => number)): number

    It's valid TypeScript, but it will interpreted as ...

    // watch out! typescript will think it means ...
    const sqk = (x: number, callback: ((number: any) => number)): number

    Ie, TypeScript will think the parameter name is number and the implied type is any. This is obviously not what we intended, but alas, that is how TypeScript works.

    So don't forget to provide the parameter names when typing your function parameters... stupid as it might seem.

    0 讨论(0)
提交回复
热议问题