2016-10-18 1 views
2

Ce tapuscrit compile bien:Comment puis-je obtenir la sécurité de type pour les fonctions renvoyées dans TypeScript?

abstract class Animal { 
    /* 
    Any extension of Animal MUST have a function which returns 
    another function that has exactly the signature (string): void 
    */ 
    abstract getPlayBehavior(): (toy: string) => void; 
} 

class Cat extends Animal { 
    /* 
    Clearly does not have a function which returns a function 
    that has the correct signature. This function returns a function with 
    the signature (void) : void 
    */ 
    getPlayBehavior() { 
     return() => { 
      console.log(`Play with toy_var_would_go_here!`); 
     }; 
    } 
} 

class Program { 
    static main() { 
     let cat: Animal = new Cat(); 
     cat.getPlayBehavior()("Toy"); 
    } 
} 

Program.main(); 

J'attends une erreur parce que la classe Cat ne met pas en œuvre certainement la classe abstraite des animaux correctement. Je suppose que la classe Cat doit avoir une fonction qui renvoie une autre fonction de la signature exacte spécifiée dans la classe Animal abstraite.

L'exécution du code, je reçois:

> node index.js 
> Play with toy_var_would_go_here! 

Est-ce que je peux faire pour vous assurer que le compilateur applique ce genre de politique?

+0

En JavaScript, une fonction nulle est une fonction unaire. Les arguments supplémentaires passés sont simplement ignorés. Ceci est commun dans, disons, la gestion des événements. Donc, votre fonction est de la bonne signature. –

+0

@FengyangWang: Je ne suis pas en désaccord, sauf pour dire que TypeScript fait beaucoup de choses qui ne sont pas émises en JavaScript uniquement pour que la sécurité du type soit appliquée. Je suis juste curieux de savoir si je peux trouver un moyen de renforcer ce type de sécurité. –

Répondre

1

Vous n'êtes pas obtenir une erreur, car en javascript/tapuscrit vous n'êtes pas obligé de déclarer les arguments si vous ne voulez pas les utiliser , tant qu'il n'y a pas de contradiction.

Par exemple, la signature du Array.forEach est:

forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; 

Mais cela compilera très bien:

let a = [1, 2, 3]; 
a.forEach(item => console.log(item)); 

Et c'est une bonne chose, il aurait été horrible si je devais avoir tous les arguments même si je ne les utilise pas.
va de même ici:

type MyFn = (s: string) => void; 
let fn: MyFn =() => console.log("hey"); 

Si je ne ai pas besoin d'utiliser l'argument de chaîne alors je peux négliger, ou je peux même le faire:

Si vous modifiez la signature de la fonction que vous revenez dans Cat.getPlayBehavior à quelque chose qui contredit la définition Animal alors vous obtiendrez une erreur:

class Cat extends Animal { 
    getPlayBehavior() { 
     return (n: number) => { 
      console.log(`Play with toy_var_would_go_here!`); 
     }; 
    } 
} 

Erreur:

Class 'Cat' incorrectly extends base class 'Animal'. 
    Types of property 'getPlayBehavior' are incompatible. 
    Type '() => (n: number) => void' is not assignable to type '() => (toy: string) => void'. 
     Type '(n: number) => void' is not assignable to type '(toy: string) => void'. 
     Types of parameters 'n' and 'toy' are incompatible. 
      Type 'string' is not assignable to type 'number'. 
+0

Personnellement, je ne trouve pas horrible d'exiger strictement que les fonctions soient définies explicitement en fonction de leur classe/interface de base. Par exemple, si une fonction a un (des) paramètre (s) optionnel (s), ceux-ci peuvent être définis explicitement comme optionnels. Si ce n'est pas un bon défaut, alors quelque chose comme des blocs strict {} serait utile pour les développeurs qui seraient plus à l'aise avec les types explicitement et strictement définis. –

+0

Eh bien, c'est comme ça que javascript fonctionne, et le tapuscrit le permet aussi. Si la fonction passée n'a pas besoin d'utiliser les arguments, elle ne les inclut pas dans la signature. Comme je l'ai dit, cela fonctionne aussi longtemps qu'il n'y a pas de contradiction. Un paramètre optionnel signifie quelque chose d'autre, cela signifie qu'il peut ou non être passé, mais ici vous dites que vous vous en fichez si ce param est passé ou non, vous n'en avez tout simplement pas besoin. –

+0

Hmm, Ok. On dirait que ce que je recherche en dehors de TypeScript n'existe pas actuellement et que mes pensées seraient mieux mises en suggestion sur leur site. Merci pour l'aide! –

1

I am expecting an error because the Cat class definitely does not implement the abstract Animal class properly

En raison de la compatibilité de type. Une fonction (disons foo) qui n'accepte aucun paramètre est assignable à une fonction (disons bar) qui prend un paramètre.

Cause: Il n'y a pas d'utilisation de bar où toutes les informations nécessaires à foo pour fonctionner seront absentes.

Plus

Ceci est également couvert ici: https://basarat.gitbooks.io/typescript/content/docs/types/type-compatibility.html#number-of-arguments

+0

Généralement, les abstractions et les interfaces sont des accords contractuels.Les implémentations les suivent explicitement à la lettre de la façon dont ces interfaces/abstractions sont définies. Si plusieurs méthodes commencent à apparaître et n'ont pas besoin d'une variable de la fonction dans une interface, la conception devrait peut-être être considérée. Je cherche un moyen d'imposer cette implémentation stricte. –