Comment utilisez-vous la saisie de canard en javascript sans toujours vérifier les propriétés et les méthodes?

11

Je sais que javascript utilise le typage canard et au début, je pensais que cela rendrait le polymorphisme facile par rapport aux langages fortement typés comme C #. Mais maintenant, mes fonctions qui prennent des arguments sont jonchées de choses comme:

if(myObj.hasSomeProperty())

ou

if(myObj.hasSomeMethod())

ou

if(isNumber(myParam))

etc.

C'est vraiment moche pour moi. Je viens d'un arrière-plan C # et je trouve que les interfaces définies sont bien meilleures.

Je me demande si j'essaie incorrectement d'appliquer des stratégies efficaces dans les langages à typage statique et qu'il existe une meilleure façon de le faire en javascript?

Je sais que je ne pouvais tout simplement pas vérifier, mais le suivi des erreurs d'exécution javascript peut être un cauchemar car elles ne se produisent pas toujours là où l'erreur se produit réellement dans le code.

Légion
la source
2
Je pense que vous pourriez simplement tâtonner sur la nature d'un langage typé dynamiquement. Vous devez vous habituer à l'état d'esprit selon lequel de nombreuses erreurs se produiront lors de l'exécution au lieu de la compilation. Si vous ressentez le besoin de vérifier si chaque argument est un nombre dans chaque fonction qui saisit des nombres, cela peut devenir un fardeau (bien que cela puisse valoir la peine si vous expédiez une bibliothèque avec la sécurité comme objectif principal). Pour quelque chose d'échelle, je trouve qu'il est essentiel d'autoriser simplement les fonctions à échouer si les mauvais types sont transmis. Au lieu de cela, une concentration plus productive pourrait être sur la construction de tests.
Où cela peut aider à faire ces vérifications pour vous assurer que les types sont conformes aux exigences d'interface nécessaires (vérifier pour voir s'ils ont les méthodes requises, par exemple) se trouve dans vos fonctions les plus centrales et les plus utilisées (celles avec instabilité = 0 avec le métrique de couplage efférent / afférent fourni par Martin). Cela devrait être une cible assez petite. Il existe généralement de nombreuses fonctions locales ponctuelles dont la portée est isolée - celles-ci n'ont probablement pas besoin d'un ensemble complet de vérifications d'exécution. Ils n'accumulent pas beaucoup de complexité.
Basculez vers Type Script. Il est toujours typé canard, mais prend en charge la saisie statique pour détecter de nombreuses erreurs au moment de la compilation.
CodesInChaos
2
Vous avez rencontré le plus gros problème du typage du canard: sa puissance vient de sa faiblesse. Si vous voulez faire du JavaScript orienté objet, il vous suffit de vivre avec les erreurs d'exécution et d'espérer que vos tests unitaires les trouveront peu de temps après les avoir créés :-(
Ross Patterson
@RossPatterson Le problème des OP est lié à la frappe dynamique, pas à la frappe de canard. TypeScript et Go sont tous deux typés canard, mais évitent le problème de l'OP. Le problème avec la frappe de canard est différent, à savoir que vous pouvez avoir des membres qui réussissent le test de canard mais ne remplissent pas le contrat que vous attendez.
CodesInChaos

Réponses:

11

Comment utilisez-vous la saisie de canard en javascript sans toujours vérifier les propriétés et les méthodes?

Simple: ne vérifiez pas toujours les propriétés et les méthodes.

Dans Ruby, ce que vous appelez est appelé "frappe de poulet". Dans une langue dynamiquement typée canard, vous avez simplement confiance que l'appelant vous passe un objet approprié. C'est le travail de l'appelant d'honorer sa partie du contrat.

Je sais que javascript utilise le typage canard et au début, je pensais que cela rendrait le polymorphisme facile par rapport aux langages fortement typés comme C #.

Vous confondez plusieurs axes orthogonaux de frappe ici. Il existe quatre axes orthogonaux de frappe:

  • Quand : typage dynamique (les types ne sont pas connus et vérifiés jusqu'à l'exécution) vs typage statique (les types sont connus et vérifiés avant l'exécution)
  • Quoi : typage canard (les types sont basés sur le comportement ), typage structurel (les types sont basés sur la structure ) et typage nominal (les types sont basés sur le nom )
  • Peux-tu les voir? typage explicite (les types doivent être explicitement annotés) vs typage implicite (les types sont inférés)
  • typage fort vs typage faible - vous avez peut-être remarqué que je n'ai pas donné à celui-ci un titre accrocheur ni une explication entre parenthèses, c'est parce que contrairement aux sept termes ci-dessus, qui ont chacun une définition précise universellement acceptée, ces deux termes ont environ une douzaine de définitions vagues semi-largement utilisées qui se contredisent; idéalement, vous devriez éviter complètement ces termes, et si vous devez les utiliser, définissez-les précisément en premier

Étant donné que vous avez mentionné C #: il est principalement typé statiquement, mais prend en charge la saisie dynamique via le type dynamic, il est principalement typé nominalement, mais les types anonymes utilisent le typage structurel, et les modèles syntaxiques (tels que la syntaxe de compréhension des requêtes LINQ) peuvent être considérés comme des canards -typé ou structurellement typé, il est principalement explicitement typé mais prend en charge le typage implicite pour les arguments de type génériques et les variables locales (bien que le cas des variables locales soit plutôt étrange par rapport à la plupart des autres langues, car vous ne pouvez pas simplement laisser le type, à la place, vous devez lui donner un pseudo-type explicitevaren d'autres termes, si vous voulez un type implicite, vous devez le dire explicitement). La question de savoir si C # est fortement ou faiblement typé dépend de la définition des deux termes que vous utilisez, cependant, notez qu'il peut y avoir beaucoup d'erreurs de type d'exécution en C #, en particulier en raison d'une covariance de tableau non sécurisée.

Je sais que je ne pouvais tout simplement pas vérifier, mais le suivi des erreurs d'exécution javascript peut être un cauchemar car elles ne se produisent pas toujours là où l'erreur se produit réellement dans le code.

Le débogage n'est pas une compétence facile à apprendre. Il existe cependant des techniques pour faciliter le débogage, par exemple le Saff Squeeze est une technique décrite par Kent Beck qui utilise des tests et une refactorisation pour le débogage:

Hit 'em High, Hit' em Low :

Test de régression et compression Saff

Kent Beck, Three Rivers Institute

Résumé: Pour isoler efficacement un défaut, commencez par un test au niveau du système et progressivement en ligne et élaguez jusqu'à ce que vous ayez le plus petit test possible qui démontre le défaut.

Jörg W Mittag
la source
Ce lien hit em high hit em low obtient un http 500 pour moi, avec "La page n'est plus disponible" comme message orienté humain.
joshp
Le domaine threeriversinstitute.org semble avoir été abandonné.
Bart van Ingen Schenau
Ah, bon sang. Et il n'est même pas archivé sur la machine WayBack .
Jörg W Mittag
Comment l'appelant est-il censé honorer sa partie du contrat? Il semble qu'il n'y ait aucun moyen de communiquer (dans le code) quels devraient être les paramètres. Chaque fonction est de la forme fonction fname (objParam, objParam, ...). Cela signifie-t-il que des langues comme javascript dépendent entièrement de la documentation externe pour communiquer l'utilisation?
Legion
@Legion: documentation, bonne dénomination, bon sens, tests comme spécifications comportementales, lecture du code source, vous l'appelez. Notez que cela n'est en fait pas très différent des systèmes de type plus faibles tels que C # ou Java: par exemple, la signification de la valeur de retour de IComparer<T>.Compare(T, T)n'est claire que dans la documentation, pas le type. Et où dans le type de java.util.Collections.binarySearch(java.util.List<T>)ça dit que le…
Jörg W Mittag
1

Je sais que je ne pouvais tout simplement pas vérifier, mais le suivi des erreurs d'exécution javascript peut être un cauchemar car elles ne se produisent pas toujours là où l'erreur se produit réellement dans le code.

En effet, la pratique typique n'est pas de vérifier. Et, oui, cela signifie que vous obtiendrez des erreurs javascript qui sont signalées ailleurs du problème réel. Mais dans la pratique, je ne trouve pas que ce soit un gros problème.

Lorsque je travaille en javascript, je teste constamment ce que j'écris. Dans la plupart des codes, j'ai des tests unitaires qui s'exécutent automatiquement chaque fois que j'enregistre mon éditeur. Quand quelque chose se passe de façon inattendue, je le sais presque immédiatement. J'ai une très petite zone de code dans laquelle j'ai peut-être fait l'erreur, car c'est presque toujours la dernière chose que j'ai touchée qui a fait l'erreur.

Lorsque j'obtiens une erreur d'exécution, j'ai au moins la trace de la pile et, dans le cas d'une erreur dans le navigateur, j'ai la possibilité d'accéder à n'importe quel niveau de la trace de la pile et d'inspecter les variables. Il est généralement facile de retracer l'origine de la mauvaise valeur, et donc de remonter au problème d'origine.

Si vous êtes comme moi lorsque j'écrivais principalement dans des langages à typage statique, j'ai écrit de plus gros blocs de code avant de tester et je n'avais pas l'habitude de retrouver une valeur d'où elle venait. La programmation dans un langage comme javascript est différente, vous devez utiliser différentes compétences. Je soupçonne qu'une programmation comme celle-ci semble beaucoup plus difficile, car ce ne sont pas les compétences que vous avez développées en travaillant dans d'autres langages comme C #.

Cela dit, je pense qu'il y a beaucoup à dire pour les types explicites. Ils sont parfaits pour la documentation et la détection précoce des erreurs. Je pense qu'à l'avenir, nous verrons l'adoption croissante de choses comme Flow et Typescript qui ajoutent la vérification de type statique au javascript.

Winston Ewert
la source
0

Je pense que vous faites la bonne chose, il vous suffit de trouver le style qui vous plaira le plus. Voici quelques idées:

  • Au lieu de if(myObj.hasSomeProperty())vous pourriez utiliser if( myobj.prop !== undefined ). Cela, BTW ne fonctionnera qu'en mode non strict, en mode strict que vous devrez utiliser if( typeof myobj.prop !== 'undefined' ).

  • Vous pouvez décharger une partie de la vérification de type sur des validateurs distincts. Cela a l'avantage de pouvoir ignorer la validation une fois que les interfaces sont matures, par exemple if( is_valid( myobject )), où is_validcommence if( !DEBUG ) return true;.

  • Parfois, il est logique de cloner les entrées dans une forme canonique, auquel cas vous pouvez collecter les différentes cibles de validation dans la fonction / l'objet de clonage. Par exemple, dans my_data = Data( myobj, otherstuff )le Dataconstructeur pourrait facilement exécuter toutes les différentes validations dans un endroit central.

  • Vous pouvez utiliser une bibliothèque qui (à un coût élevé) rationalisera votre validation de type en quelque chose de plus élégant. Même si vous ne suivrez pas cette voie à long terme, vous trouverez peut-être confortable de vous mettre en douceur dans votre propre style. Certains exemples incluent xtype.js , type-check , validator.js , etc.

avnr
la source