
inb4: Esta no es otra "configuración" de un nuevo proyecto con el tutorial Vue y TypeScript. ¡Profundicemos en temas más complejos!
typescript
es asombroso. Vue
es asombroso. Sin duda, mucha gente intenta agruparlos . Pero, debido a diferentes razones, es difícil escribir realmente su aplicación Vue
. Veamos cuáles son los problemas y qué se puede hacer para resolverlos (o al menos minimizar el impacto).
TLDR
Tenemos esta maravillosa plantilla con Nuxt
, Vue
, Vuex
y jest
completamente tipeados. Simplemente instálelo y todo estará cubierto para usted. Ve a los documentos para obtener más información.
Y como dije, no voy a guiarte a través de la configuración básica por tres razones:
- Hay muchos tutoriales existentes al respecto
- Hay muchas herramientas para comenzar con un solo clic, como
Nuxt
y vue-cli
con el complemento typescript
- Ya tenemos
wemake-vue-template
donde cada parte de la configuración de la que voy a hablar ya está cubierta
Tipificación de componentes
La primera expectativa rota cuando comienza a trabajar con Vue
y el typescript
y después de haber escrito los componentes de su clase es que las etiquetas <template>
y <style>
todavía no se escriben. Déjame mostrarte un ejemplo:
<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>
He hecho dos errores tipográficos aquí: {{ usr }}
lugar de {{ user }}
y $style.headr
lugar de $style.header
. ¿El typescript
me salvará de estos errores? No, no lo hará.
¿Qué se puede hacer para solucionarlo? Bueno, hay varios hacks.
Escribiendo la plantilla
Se puede usar Vetur
con la opción vetur.experimental.templateInterpolationService
para verificar sus plantillas. Sí, esto es solo una verificación basada en el editor y todavía no se puede usar dentro del CI. Pero, el equipo de Vetur
está trabajando duro para proporcionar una CLI que permita esto. Rastree el problema original en caso de que esté interesado.

La segunda opción son dos pruebas de instantáneas de escritura con jest
. Capturará muchos errores basados en plantillas. Y es bastante barato en el mantenimiento.
Por lo tanto, la combinación de estas dos herramientas le brinda una agradable experiencia de desarrollador con comentarios rápidos y una forma confiable de detectar errores dentro del CI.
Estilos de escritura
Escribir css-module
s también está cubierto por varias herramientas externas:
La idea principal de estas herramientas es buscar css-module
y luego crear archivos de declaración .d.ts
partir de ellos. Entonces sus estilos estarán completamente escritos. Todavía no está implementado para Nuxt
o Vue
, pero puede Nuxt
este problema para avanzar.
Sin embargo, personalmente no uso ninguna de estas herramientas en mis proyectos. Pueden ser útiles para proyectos con bases de código grandes y muchos estilos, pero estoy bien con solo instantáneas.
Las guías de estilo con pruebas de regresión visual también ayudan mucho. @storybook/addon-storyshots
es un buen ejemplo de esta técnica.
Vuex
La próxima gran cosa es Vuex
. Tiene cierta complejidad de diseño incorporada para escribir:
const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 })
El problema es que 'action_name'
podría no existir, tomar otros argumentos o devolver un tipo diferente. Eso no es algo que esperes para una aplicación completamente tipada.
¿Cuáles son las soluciones existentes?
clase vuex
vuex-class
es un conjunto de decoradores para permitir un fácil acceso desde sus componentes basados en clase a los Vuex
internos de Vuex
.
Pero no está tipificado como seguro ya que no puede interferir con los tipos de estado, captadores, mutaciones y acciones.

Por supuesto, puede anotar manualmente tipos de propiedades.

Pero, ¿qué vas a hacer cuando cambie el tipo real de tu estado, captadores, mutaciones o acciones? Tendrá una discrepancia de tipo oculto.
vuex-simple
Ahí es donde vuex-simple
nos ayuda. En realidad, ofrece una forma completamente diferente de escribir su código Vuex
y eso es lo que lo hace seguro. Echemos un vistazo:
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 } }
Más tarde, este módulo escrito puede registrarse dentro de su Vuex
la Vuex
manera:
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)
Ahora tenemos una instancia de Vuex.Store
100% nativa y toda la información de tipo incluida. Para usar esta tienda escrita en el componente, podemos escribir solo una línea de código:
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 }
Ahora hemos escrito Vuex
que se puede usar de forma segura dentro de nuestro proyecto.
Cuando cambiamos algo dentro de la definición de nuestra tienda, se refleja automáticamente en los componentes que usan esta tienda. Si algo falla, lo sabemos lo antes posible.
También hay diferentes bibliotecas que hacen lo mismo pero tienen API diferentes. Elige lo que más te convenga.
Llamadas a la API
Cuando tenemos Vuex
correctamente configurado, necesitamos llenarlo con datos.
Echemos un vistazo a nuestra definición de acción nuevamente:
@Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList }
¿Cómo podemos saber que realmente devolverá una lista de CommentType
y no un solo number
o un montón de instancias de AuthorType
?
No podemos controlar el servidor. Y el servidor podría realmente romper el contrato. O simplemente podemos pasar la instancia de api
incorrecta, hacer un error tipográfico en la URL, o lo que sea.
¿Cómo podemos estar seguros? ¡Podemos usar la escritura en tiempo de ejecución! Déjame presentarte 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 hacemos aqui
- Definimos una instancia de
ts.type
con campos que necesitamos verificar en tiempo de ejecución cuando recibimos una respuesta del servidor - Definimos un tipo estático para ser utilizado en la anotación sin ninguna placa adicional
Y luego podemos usarlo en nuestras llamadas a la 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) }
Con la ayuda de io-ts-promise
, podemos devolver una Promise
en un estado fallido si la respuesta del servidor no coincide con un tipo ts.array(Comment)
. Realmente funciona como una validación.
fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */)
Además, la anotación de tipo de retorno está sincronizada con el método .decode
. Y no puedes poner tonterías al azar allí:

Con la combinación de tiempo de ejecución y comprobaciones estáticas, podemos estar seguros de que nuestras solicitudes no fallarán debido a la falta de coincidencia de tipos.
Pero, para estar 100% seguro de que todo funciona, recomendaría usar pruebas basadas en contratos: eche un vistazo al pact
como ejemplo. Y supervise su aplicación con Sentry
.
Enrutador Vue
El siguiente problema es que this.$router.push({ name: 'wrong!' })
No funciona de la manera que queremos.
Diría que sería ideal que el compilador nos advirtiera que nos estamos dirigiendo a la dirección incorrecta y que esta ruta no existe.
Pero no es posible. Y no se puede hacer mucho: hay muchas rutas dinámicas, expresiones regulares, fallbacks, permisos, etc. que eventualmente pueden romperse. La única opción es probar cada una de this.$router
llamadas this.$router
en su aplicación.
vue-test-utils
Hablando de pruebas, no tengo excusas para no mencionar @vue/test-utils
que también tiene algunos problemas con la escritura.
Cuando intentemos probar nuestro nuevo componente brillante con la propiedad typedStore
, descubriremos que en realidad no podemos hacerlo de acuerdo con el typescript
:

¿Por qué sucede esto? Ocurre porque la llamada mount()
no sabe nada sobre el tipo de su componente, porque todos los componentes tienen un tipo VueConstructor<Vue>
de forma predeterminada:

De ahí provienen todos los problemas. Que se puede hacer
Puede usar vuetype
para producir los YouComponent.vue.d.ts
YouComponent.vue.d.ts que le indicarán a sus pruebas el tipo exacto del componente montado.
También puede realizar un seguimiento de este problema para el progreso.
Pero no me gusta esta idea. Estas son pruebas, pueden fallar. No es gran cosa.
Es por eso que me (wrapper.vm as any).whatever
a (wrapper.vm as any).whatever
enfoque. Esto me ahorra bastante tiempo para escribir pruebas. Pero estropea un poco la experiencia del desarrollador.
Toma tu propia decisión aquí:
- Usa
vuetype
todo el tiempo - Aplíquelo parcialmente a los componentes más importantes con la mayor cantidad de pruebas y actualícelo regularmente
- Use
any
como reserva
Conclusión
El nivel promedio de soporte de typescript
en el ecosistema Vue
aumentó en los últimos años:
Nuxt
introdujo por primera vez nuxt-ts
y ahora envía las compilaciones ts
por defectoVue@3
tendrá soporte mejorado de typescript
- Más aplicaciones y complementos de terceros proporcionarán definiciones de tipo
Pero, la producción está lista en este momento. ¡Estas son solo cosas para mejorar! Escribir código Vue
seguridad de tipo realmente mejora su experiencia de desarrollador y le permite concentrarse en las cosas importantes mientras deja el trabajo pesado al compilador.
¿Cuáles son tus hacks y herramientas favoritas para escribir aplicaciones Vue
? Discutámoslo en la sección de comentarios.