Typoskript: Definieren des Typs für einen Funktionsrückruf (als beliebiger Funktionstyp, nicht als universeller), der in einem Methodenparameter verwendet wird

313

Derzeit habe ich eine Typdefinition als:

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

Ich brauche so etwas wie:

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

aber der 2. wird nicht akzeptiert.

Smrutiranjan Sahu
quelle

Antworten:

285

Der globale Typ Functiondient diesem Zweck.

Wenn Sie diesen Rückruf mit 0 Argumenten aufrufen möchten und seinen Rückgabewert ignorieren, () => voidstimmt der Typ außerdem mit allen Funktionen überein, die keine Argumente annehmen.

Ryan Cavanaugh
quelle
27
Dieses Ding fehlt in Grundtypen
Yogesh
13
Es ist kein Basistyp, da Sie Ihre Argumente definieren und Werte zurückgeben sollten. so etwas wie Rückruf: (Nummer: Nummer) => void; ist für die Typprüfung viel nützlicher als der Rückruf: function; wäre.
Kpup
@kpup Um klar zu sein, sagen Sie, dass Sie das Großbuchstaben F nicht verwenden sollen, Functionwie in der ersten Zeile dieser Antwort gezeigt, und dass der zweite Absatz (mit dem Typ () => voidoder was auch immer zum Anwendungsfall passt) bevorzugt wird?
Ruffin
2
FWIW, Dokumente zu Funktionstypen sind hier
imjared
191

Typescript aus Version 1.4 hat das typeSchlüsselwort, das einen Typalias deklariert (analog zu a typedefin C / C ++). Sie können Ihren Rückruftyp folgendermaßen deklarieren:

type CallbackFunction = () => void;

Dies deklariert eine Funktion, die keine Argumente akzeptiert und nichts zurückgibt. Eine Funktion, die null oder mehr Argumente eines beliebigen Typs akzeptiert und nichts zurückgibt, wäre:

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

Dann können Sie zum Beispiel sagen:

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

Wenn Sie eine Funktion wünschen, die eine beliebige Anzahl von Argumenten akzeptiert und alles zurückgibt (einschließlich void):

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

Sie können einige obligatorische Argumente und dann eine Reihe zusätzlicher Argumente (z. B. eine Zeichenfolge, eine Zahl und dann eine Reihe zusätzlicher Argumente) wie folgt angeben:

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

Dies kann beispielsweise für EventEmitter-Handler hilfreich sein.

Funktionen können auf diese Weise so stark eingegeben werden, wie Sie möchten. Sie können jedoch mitgerissen werden und auf kombinatorische Probleme stoßen, wenn Sie versuchen, alles mit einem Typ-Alias ​​festzunageln.

David G.
quelle
1
Zwischen Functionund (...args: any[]) => anywas wird bevorzugt?
Ahong
@ahong: Persönlich würde ich Letzteres bevorzugen, da es eine Signatur liefert ... normalerweise. ...args: any[]ist nicht sehr nützlich.
Ed S.
type CallbackFunctionSomeVariadic = (arg1: string, arg2: number, ...args: any[]) => void;was ich gesucht habe, ty.
Aqteifan
61

Nach Ryans Antwort denke ich, dass die gesuchte Schnittstelle wie folgt definiert ist:

interface Param {
    title: string;
    callback: () => void;
}
Blorkfish
quelle
34

Hier ist ein Beispiel für eine Funktion, die einen Rückruf akzeptiert

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
});

Wenn Sie sich nicht für die Rückgabewerte von Rückrufen interessieren (die meisten Leute wissen nicht, wie sie effektiv eingesetzt werden sollen), können Sie sie verwenden 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
});

Beachten Sie, dass die Signatur, die ich für den callbackParameter verwendet habe ...

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

Ich würde sagen, dass dies ein TypeScript-Mangel ist, da erwartet wird, dass wir einen Namen für die Rückrufparameter angeben. In diesem Fall habe ich verwendet, _weil es innerhalb der sqkFunktion nicht verwendbar ist .

Wenn Sie dies jedoch tun

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

Es ist gültiges TypeScript, wird aber interpretiert als ...

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

Dh, wird denken , Typoskript der Parametername ist numberund die implizite Typ ist any. Dies ist offensichtlich nicht das, was wir beabsichtigt haben, aber leider funktioniert TypeScript so.

Vergessen Sie also nicht, die Parameternamen anzugeben, wenn Sie Ihre Funktionsparameter eingeben ... so dumm es auch scheinen mag.

Danke dir
quelle
32

Sie können einen Funktionstyp in der Schnittstelle auf verschiedene Arten definieren:

  1. allgemeiner Weg:
export interface IParam {
  title: string;
  callback(arg1: number, arg2: number): number;
}
  1. Wenn Sie dann die Eigenschaftssyntax verwenden möchten,
export interface IParam {
  title: string;
  callback: (arg1: number, arg2: number) => number;
}
  1. Wenn Sie zuerst den Funktionstyp deklarieren, dann
type MyFnType = (arg1: number, arg2: number) => number;

export interface IParam {
  title: string;
  callback: MyFnType;
}

Die Verwendung ist sehr einfach,

function callingFn(paramInfo: IParam):number {
    let needToCall = true;
    let result = 0;
   if(needToCall){
     result = paramInfo.callback(1,2);
    }

    return result;
}
  1. Sie können auch ein Funktionstyp-Literal deklarieren, was bedeutet, dass eine Funktion eine andere Funktion als Parameter akzeptieren kann. Die Parametrierungsfunktion kann auch als Rückruf aufgerufen werden.
export interface IParam{
  title: string;
  callback(lateCallFn?:
             (arg1:number,arg2:number)=>number):number;

}
Humayoun_Kabir
quelle
10

Es gibt vier abstrakte Funktionstypen. Sie können diese separat verwenden, wenn Sie wissen, dass Ihre Funktion Argumente akzeptiert oder nicht, Daten zurückgibt oder nicht.

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

so was:

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

Um nur einen Typ als Funktionstyp zu verwenden, können wir alle abstrakten Typen wie folgt miteinander kombinieren:

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

dann benutze es wie:

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

Im obigen Beispiel ist alles korrekt. Das unten stehende Verwendungsbeispiel ist jedoch aus Sicht der meisten Code-Editoren nicht korrekt.

// 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();
}

Der richtige Aufruf für Redakteure lautet wie folgt:

public callArgument(callback: fFunction) {

    // pay attention in this part, for fix editor(s) error
    (callback as fFunction)();
}
Artur T.
quelle
2

Typoskript: Wie definiere ich den Typ für einen Funktionsrückruf, der in einem Methodenparameter verwendet wird?

Sie können den Rückruf als 1) Funktionseigenschaft oder 2) Methode deklarieren :

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

interface ParamMethod {
    callback(a: Animal): void; // method
}

Seit TS 2.6 gibt es einen wichtigen Tippunterschied :

Sie erhalten stärkere ("Sound") Typen im --strictoder --strictFunctionTypesModus, wenn eine Funktionseigenschaft deklariert wird. Nehmen wir ein Beispiel:

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 ...

Technisch gesehen sind Methoden bivariant und Funktionseigenschaften in ihren Argumenten unter kontravariantstrictFunctionTypes . Methoden werden immer noch freizügiger überprüft (auch wenn sie nicht klingen), um in Kombination mit eingebauten Typen wie etwas praktischer zu sein Array.

Zusammenfassung

  • Es gibt einen Typunterschied zwischen Funktionseigenschaft und Methodendeklaration
  • Wählen Sie nach Möglichkeit eine Funktionseigenschaft für stärkere Typen

Beispielcode für den Spielplatz

ford04
quelle