Vue vraiment typée

Le logo


inb4: Ce n'est pas une autre "mise en place" d'un nouveau projet avec Tutoriel Vue et TypeScript. Faisons un peu de plongée dans des sujets plus complexes!


typescript est génial. Vue est géniale. Nul doute que beaucoup de gens essaient de les regrouper . Mais, pour différentes raisons, il est difficile de vraiment taper votre application Vue . Voyons quels sont les problèmes et ce qui peut être fait pour les résoudre (ou au moins minimiser l'impact).


TLDR


Nous avons ce merveilleux modèle avec Nuxt , Vue , Vuex et jest entièrement tapé. Il suffit de l'installer et tout sera couvert pour vous. Accédez à la documentation pour en savoir plus.


Et comme je l'ai dit, je ne vais pas vous guider dans la configuration de base pour trois raisons:


  1. Il existe de nombreux tutoriels à ce sujet
  2. Il existe de nombreux outils pour démarrer en un seul clic comme Nuxt et vue-cli avec le plugin typescript
  3. Nous avons déjà wemake-vue-template où chaque wemake-vue-template de configuration dont je vais parler est déjà couvert

Typage des composants


La première attente brisée lorsque vous commencez à travailler avec Vue et typescript et après avoir déjà tapé vos composants de classe est que les balises <template> et <style> sont toujours pas tapées. Permettez-moi de vous montrer un exemple:


 <template> <h1 :class="$style.headr"> Hello, {{ usr }}! </h1> </template> <script lang="ts"> import Vue from 'vue' import Component from 'vue-class-component' import { Prop } from 'vue-property-decorator' @Component({}) export default class HelloComponent extends Vue { @Prop() user!: string } </script> <style module> .header { /* ... */ } </style> 

J'ai fait deux fautes de frappe ici: {{ usr }} au lieu de {{ user }} et $style.headr au lieu de $style.header . Est-ce que typescript me sauvera de ces erreurs? Non, ce ne sera pas le cas.


Que peut-on faire pour y remédier? Eh bien, il y a plusieurs hacks.


Taper le modèle


On peut utiliser Vetur avec l'option vetur.experimental.templateInterpolationService pour vérifier vos modèles. Oui, il ne s'agit que d'une vérification basée sur l'éditeur et il ne peut pas encore être utilisé à l'intérieur du CI. Mais, l'équipe Vetur travaille dur pour fournir une CLI permettant cela. Suivez le problème d'origine au cas où vous seriez intéressé.


vetur


La deuxième option est deux tests d'écriture de cliché avec jest . Il détectera de nombreuses erreurs basées sur des modèles. Et c'est assez bon marché dans l'entretien.


Ainsi, la combinaison de ces deux outils vous offre une belle expérience de développeur avec des commentaires rapides et un moyen fiable de détecter les erreurs à l'intérieur du CI.


Styles de saisie


La saisie de css-module s est également couverte par plusieurs outils externes:



L'idée principale de ces outils est de récupérer les css-module puis de créer des fichiers de déclaration .d.ts à partir d'eux. Ensuite, vos styles seront entièrement saisis. Il n'est toujours pas implémenté pour Nuxt ou Vue , mais vous pouvez traiter ce problème pour progresser.


Cependant, je n'utilise personnellement aucun de ces outils dans mes projets. Ils peuvent être utiles pour des projets avec de grandes bases de code et beaucoup de styles, mais je suis d'accord avec seulement des instantanés.


Les guides de style avec des tests de régression visuelle aident également beaucoup. @storybook/addon-storyshots est un bel exemple de cette technique.


Vuex


La prochaine grande chose est Vuex . Il a une certaine complexité de conception intégrée pour la frappe:


 const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 }) 

Le problème est que 'action_name' peut ne pas exister, prendre d'autres arguments ou renvoyer un type différent. Ce n'est pas quelque chose que vous attendez d'une application entièrement typée.


Quelles sont les solutions existantes?


classe vuex


vuex-class est un ensemble de décorateurs pour permettre un accès facile de vos composants basés sur la Vuex aux Vuex internes de Vuex .


Mais, il n'est pas dactylographié sûr car il ne peut pas interférer avec les types d'état, les getters, les mutations et les actions.


classe vuex


Bien sûr, vous pouvez annoter manuellement les types de propriétés.


vuex-class annoté


Mais qu'allez-vous faire lorsque le type réel de votre état, de vos getters, de vos mutations ou de vos actions changera? Vous aurez un décalage de type caché.


vuex-simple


C'est là que vuex-simple nous aide. Il offre en fait une façon complètement différente d'écrire votre code Vuex et c'est ce qui le rend sûr. Jetons un coup d'œil:


 import { Action, Mutation, State, Getter } from 'vuex-simple' class MyStore { // State @State() public comments: CommentType[] = [] // Getters @Getter() public get hasComments (): boolean { return Boolean(this.comments && this.comments.length > 0) } // Mutations @Mutation() public setComments (payload: CommentType[]): void { this.comments = updatedComments } // Actions @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() this.setComments(commentsList) // typed mutation return commentsList } } 

Plus tard, ce module tapé peut être enregistré dans votre Vuex comme ceci:


 import Vue from 'vue' import Vuex from 'vuex' import { createVuexStore } from 'vuex-simple' import { MyStore } from './store' Vue.use(Vuex) // Creates our typed module instance: const instance = new MyStore() // Returns valid Vuex.Store instance: export default createVuexStore(instance) 

Nous avons maintenant une instance Vuex.Store 100% native et toutes les informations de type qui y sont regroupées. Pour utiliser ce magasin typé dans le composant, nous pouvons écrire une seule ligne de code:


 import Vue from 'vue' import Component from 'nuxt-class-component' import { useStore } from 'vuex-simple' import MyStore from './store' @Component({}) export default class MyComponent extends Vue { // That's all we need! typedStore: MyStore = useStore(this.$store) // Demo: will be typed as `Comment[]`: comments = typedStore.comments } 

Nous avons maintenant tapé Vuex qui peut être utilisé en toute sécurité dans notre projet.
Lorsque nous modifions quelque chose dans notre définition de magasin, il est automatiquement répercuté sur les composants qui utilisent ce magasin. Si quelque chose échoue - nous le savons dès que possible.


Il existe également différentes bibliothèques qui font la même chose mais ont des API différentes. Choisissez ce qui vous convient le mieux.


Appels API


Lorsque nous avons correctement configuré Vuex , nous devons le remplir de données.
Revoyons à nouveau notre définition d'action:


 @Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList } 

Comment pouvons-nous savoir qu'il retournera vraiment une liste de CommentType et non un seul number ou un tas d'instances AuthorType ?


Nous ne pouvons pas contrôler le serveur. Et le serveur pourrait en fait rompre le contrat. Ou nous pouvons simplement passer la mauvaise instance de l' api , faire une faute de frappe dans l'URL, ou autre chose.


Comment pouvons-nous être en sécurité? Nous pouvons utiliser le typage d'exécution! Permettez-moi de vous présenter io-ts :


 import * as ts from 'io-ts' export const Comment = ts.type({ 'id': ts.number, 'body': ts.string, 'email': ts.string, }) // Static TypeScript type, that can be used as a regular `type`: export type CommentType = ts.TypeOf<typeof Comment> 

Que faisons-nous ici?


  1. Nous définissons une instance de ts.type avec des champs que nous devons vérifier lors de l'exécution lorsque nous recevons une réponse du serveur
  2. Nous définissons un type statique à utiliser en annotation sans passe-partout supplémentaire

Et plus tard, nous pouvons utiliser nos appels api :


 import * as ts from 'io-ts' import * as tPromise from 'io-ts-promise' public async fetchComments (): Promise<CommentType[]> { const response = await axios.get('comments') return tPromise.decode(ts.array(Comment), response.data) } 

Avec l'aide de io-ts-promise , nous pouvons retourner une Promise dans un état d'échec si la réponse du serveur ne correspond pas au type ts.array(Comment) . Cela fonctionne vraiment comme une validation.


 fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */) 

De plus, l'annotation de type de retour est synchronisée avec la méthode .decode . Et vous ne pouvez pas y mettre des bêtises aléatoires:


io-ts


Avec la combinaison de l'exécution et des vérifications statiques, nous pouvons être sûrs que nos demandes n'échoueront pas en raison de la non-concordance de type.
Mais, pour être sûr à 100% que tout fonctionne, je recommanderais d'utiliser des tests basés sur les contrats: jetez un œil au pact comme exemple. Et surveillez votre application avec Sentry .


Routeur Vue


Le problème suivant est que this.$router.push({ name: 'wrong!' }) Ne fonctionne pas comme nous le voulons.


Je dirais que ce serait idéal d'être averti par le compilateur que nous routons dans la mauvaise direction et que cette route n'existe pas.
Mais ce n'est pas possible. Et pas grand-chose ne peut être fait: il y a beaucoup de routes dynamiques, d'expressions régulières, de replis, d'autorisations, etc. La seule option est de tester chacun this.$router Appel du this.$router dans votre application.


vue-test-utils


En parlant de tests, je n'ai aucune excuse pour ne pas mentionner @vue/test-utils qui a également quelques problèmes de frappe.


Lorsque nous essaierons de tester notre nouveau composant brillant avec la propriété typedStore , nous découvrirons que nous ne pouvons réellement pas le faire selon le typescript :


vue-test-utils


Pourquoi cela se produit-il? Cela se produit car l'appel mount() ne sait rien du type de votre composant, car tous les composants ont un type VueConstructor<Vue> par défaut:


vue-constructeur


C'est de là que viennent tous les problèmes. Que peut-on faire?
Vous pouvez utiliser vuetype pour produire des YouComponent.vue.d.ts YouComponent.vue.d.ts qui indiqueront à vos tests le type exact du composant monté.


Vous pouvez également suivre ce problème pour la progression.


Mais, je n'aime pas cette idée. Ce sont des tests, ils peuvent échouer. Pas grave.
C'est pourquoi je m'en tiens à (wrapper.vm as any).whatever . (wrapper.vm as any).whatever approche. Cela me fait gagner beaucoup de temps pour écrire des tests. Mais gâche un peu l'expérience des développeurs.


Prenez votre propre décision ici:


  • Utilisez le vuetype
  • Appliquez-le partiellement aux composants les plus importants avec le plus grand nombre de tests et mettez-le à jour régulièrement
  • Utilisez any comme solution de rechange

Conclusion


Le niveau moyen de prise en charge des caractères typescript dans l'écosystème Vue augmenté au cours des deux dernières années:


  • Nuxt abord introduit nuxt-ts et expédie maintenant les builds ts par défaut
  • Vue@3 aura une prise en charge améliorée du typescript
  • Plus d'applications et de plugins tiers fourniront des définitions de type

Mais, c'est la production prête pour le moment. Ce ne sont que des choses à améliorer! L'écriture de code Vue type sécurisé améliore vraiment votre expérience de développeur et vous permet de vous concentrer sur les choses importantes tout en laissant le gros du travail au compilateur.


Quels sont vos hacks et outils préférés pour taper les applications Vue ? Discutons-en dans la section des commentaires.

Source: https://habr.com/ru/post/fr458632/


All Articles