web-dev-qa-db-fra.com

Les fonctions fortement typées en tant que paramètres sont-elles possibles dans TypeScript?

Dans TypeScript, je peux déclarer un paramètre d'une fonction en tant que type Function. Y a-t-il un moyen de faire cela qui ne me manque pas? Par exemple, considérons ceci:

class Foo {
    save(callback: Function) : void {
        //Do the save
        var result : number = 42; //We get a number from the save operation
        //Can I at compile-time ensure the callback accepts a single parameter of type number somehow?
        callback(result);
    }
}

var foo = new Foo();
var callback = (result: string) : void => {
    alert(result);
}
foo.save(callback);

Le rappel de sauvegarde n'est pas sûr, mais je lui attribue une fonction de rappel dans laquelle le paramètre de la fonction est une chaîne, mais je lui passe un nombre et la compile sans erreur. Puis-je créer le paramètre result dans une fonction de type-safe?

TL; version DR: existe-t-il un équivalent d'un délégué .NET dans TypeScript?

471
vcsjones

Sûr:

class Foo {
    save(callback: (n: number) => any) : void {
        callback(42);
    }
}
var foo = new Foo();

var strCallback = (result: string) : void => {
    alert(result);
}
var numCallback = (result: number) : void => {
    alert(result.toString());
}

foo.save(strCallback); // not OK
foo.save(numCallback); // OK

Si vous le souhaitez, vous pouvez définir un type pour encapsuler ceci:

type NumberCallback = (n: number) => any;

class Foo {
    // Equivalent
    save(callback: NumberCallback) : void {
        callback(42);
    }
}
691
Ryan Cavanaugh

Voici les équivalents TypeScript de certains délégués .NET courants:

interface Action<T>
{
    (item: T): void;
}

interface Func<T,TResult>
{
    (item: T): TResult;
}
84
Drew Noakes

Je me rends compte que cet article est vieux, mais il existe une approche plus compacte qui est légèrement différente de celle qui a été demandée, mais peut être une alternative très utile. Vous pouvez essentiellement déclarer la fonction en ligne lors de l'appel de la méthode (Foo's save() dans ce cas). Cela ressemblerait à ceci:

class Foo {
    save(callback: (n: number) => any) : void {
        callback(42)
    }

    multipleCallbacks(firstCallback: (s: string) => void, secondCallback: (b: boolean) => boolean): void {
        firstCallback("hello world")

        let result: boolean = secondCallback(true)
        console.log("Resulting boolean: " + result)
    }
}

var foo = new Foo()

// Single callback example.
// Just like with @RyanCavanaugh's approach, ensure the parameter(s) and return
// types match the declared types above in the `save()` method definition.
foo.save((newNumber: number) => {
    console.log("Some number: " + newNumber)

    // This is optional, since "any" is the declared return type.
    return newNumber
})

// Multiple callbacks example.
// Each call is on a separate line for clarity.
// Note that `firstCallback()` has a void return type, while the second is boolean.
foo.multipleCallbacks(
    (s: string) => {
         console.log("Some string: " + s)
    },
    (b: boolean) => {
        console.log("Some boolean: " + b)
        let result = b && false

        return result
    }
)

L'approche multipleCallback() est très utile pour des appels tels que les appels réseau qui peuvent réussir ou échouer. En supposant de nouveau un exemple d’appel réseau, lorsque vous appelez multipleCallbacks(), vous pouvez définir le comportement d’un succès ou d’un échec, ce qui offre une plus grande clarté aux futurs lecteurs de code.

De manière générale, selon mon expérience, cette approche se prête à être plus concise, moins encombrée et une plus grande clarté.

Bonne chance à tous!

14
kbpontius
type FunctionName = (n: inputType) => any;

class ClassName {
    save(callback: FunctionName) : void {
        callback(data);
    }
}

Cela correspond certainement au paradigme de la programmation fonctionnelle.

12
kg11

Dans TS, nous pouvons taper des fonctions de la manière suivante:

Types de fonctions/signatures

Ceci est utilisé pour de réelles implémentations de fonctions/méthodes, il a la syntaxe suivante:

(arg1: Arg1type, arg2: Arg2type) : ReturnType

Exemple:

function add(x: number, y: number): number {
    return x + y;
}

class Date {
  setTime(time: number): number {
   // ...
  }

}

littéraux de type de fonction

Les littéraux de type de fonction sont un autre moyen de déclarer le type d'une fonction. Ils sont généralement appliqués dans la signature de fonction d'une fonction d'ordre supérieur. Une fonction d'ordre supérieur est une fonction qui accepte des fonctions en tant que paramètres ou qui renvoie une fonction. Il a la syntaxe suivante:

(arg1: Arg1type, arg2: Arg2type) => ReturnType

Exemple:

type FunctionType1 = (x: string, y: number) => number;

class Foo {
    save(callback: (str: string) => void) {
       // ...
    }

    doStuff(callback: FunctionType1) {
       // ...
    }

}
1

Si vous définissez le type de fonction en premier, il ressemblerait à

type Callback = (n: number) => void;

class Foo {
    save(callback: Callback) : void {        
        callback(42);
    }
}

var foo = new Foo();
var stringCallback = (result: string) : void => {
    console.log(result);
}

var numberCallback = (result: number) : void => {
    console.log(result);
}

foo.save(stringCallback); //--will be showing error
foo.save(numberCallback);

Sans le type de fonction utilisant la syntaxe de propriété simple, ce serait:

class Foo {
    save(callback: (n: number) => void) : void {        
        callback(42);
    }
}

var foo = new Foo();
var stringCallback = (result: string) : void => {
    console.log(result);
}

var numberCallback = (result: number) : void => {
    console.log(result);
}

foo.save(stringCallback); //--will be showing error
foo.save(numberCallback);

Si vous voulez utiliser une fonction d’interface telle que les délégués génériques c #, c’est:

interface CallBackFunc<T, U>
{
    (input:T): U;
};

class Foo {
    save(callback: CallBackFunc<number,void>) : void {        
        callback(42);
    }
}

var foo = new Foo();
var stringCallback = (result: string) : void => {
    console.log(result);
}

var numberCallback = (result: number) : void => {
    console.log(result);
}

let strCBObj:CallBackFunc<string,void> = stringCallback;
let numberCBObj:CallBackFunc<number,void> = numberCallback;

foo.save(strCBObj); //--will be showing error
foo.save(numberCBObj);
0
Humayoun_Kabir

Outre ce que d'autres ont dit, un problème courant consiste à déclarer les types de la même fonction surchargés. Le cas typique est la méthode EventEmitter on () qui accepte plusieurs types d’auditeurs. Une situation similaire pourrait se produire lorsque vous travaillez avec des actions redux - et vous utilisez le type d'action littéral pour marquer la surcharge. Dans le cas de EventEmitters, vous utilisez le type littéral de nom d'événement:

interface MyEmitter extends EventEmitter {
  on(name:'click', l: ClickListener):void
  on(name:'move', l: MoveListener):void
  on(name:'die', l: DieListener):void
  //and a generic one
  on(name:string, l:(...a:any[])=>any):void
}

type ClickListener = (e:ClickEvent)=>void
type MoveListener = (e:MoveEvent)=>void
... etc

// will type check the correct listener when writing something like:
myEmitter.on('click', e=>...<--- autocompletion
0
cancerbero