
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:
- Il existe de nombreux tutoriels à ce sujet
- Il existe de nombreux outils pour démarrer en un seul clic comme
Nuxt
et vue-cli
avec le plugin typescript
- 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é.

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.

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

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?
- 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 - 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:

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
:

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:

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éfautVue@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.