
inb4: Este não é outro "configurar" um novo projeto com o tutorial Vue e TypeScript. Vamos mergulhar fundo em tópicos mais complexos!
typescript
é incrível. Vue
é incrível. Sem dúvida, muitas pessoas tentam agrupá-las . Mas, devido a diferentes motivos, é difícil realmente digitar seu aplicativo Vue
. Vamos descobrir quais são os problemas e o que pode ser feito para resolvê-los (ou pelo menos minimizar o impacto).
TLDR
Temos este modelo maravilhoso com Nuxt
, Vue
, Vuex
e jest
totalmente digitados. Basta instalá-lo e tudo será coberto por você. Acesse os documentos para saber mais.
E como eu disse, não vou orientá-lo na configuração básica por três razões:
- Existem muitos tutoriais existentes sobre isso
- Existem muitas ferramentas para começar com um único clique, como o
Nuxt
e o vue-cli
com o plug-in de Nuxt
- Já temos o
wemake-vue-template
onde todas as configurações sobre as quais eu vou falar já estão cobertas
Tipologias de componentes
A primeira expectativa frustrada quando você começa a trabalhar com o Vue
e o typescript
e depois de digitar os componentes da classe é que as tags <template>
e <style>
ainda não foram digitadas. Deixe-me mostrar um exemplo:
<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>
Fiz dois erros de digitação aqui: {{ usr }}
vez de {{ user }}
e $style.headr
vez de $style.header
. O texto typescript
me salvará desses erros? Não, não vai.
O que pode ser feito para corrigi-lo? Bem, existem vários hacks.
Digitando o modelo
Pode-se usar o Vetur
com a opção vetur.experimental.templateInterpolationService
para verificar seus modelos. Sim, essa é apenas uma verificação baseada no editor e ainda não pode ser usada dentro do IC. Mas, a equipe da Vetur
está trabalhando duro para fornecer uma CLI para permitir isso. Acompanhe a edição original , caso esteja interessado.

A segunda opção é dois testes de gravação de captura instantânea com jest
. Ele detectará muitos erros baseados em modelo. E é muito barato em manutenção.
Portanto, a combinação dessas duas ferramentas fornece uma boa experiência para o desenvolvedor com feedback rápido e uma maneira confiável de detectar erros dentro do IC.
Estilos de digitação
Digitar css-module
s também é coberto por várias ferramentas externas:
A idéia principal dessas ferramentas é buscar o css-module
e criar arquivos de declaração .d.ts
partir deles. Seus estilos serão totalmente digitados. Ainda não foi implementado para o Nuxt
ou o Vue
, mas você pode Nuxt
esse problema para avançar.
No entanto, eu pessoalmente não uso nenhuma dessas ferramentas em meus projetos. Eles podem ser úteis para projetos com grandes bases de código e muitos estilos, mas estou bem apenas com instantâneos.
Guias com testes de regressão visual também ajudam muito. @storybook/addon-storyshots
é um bom exemplo dessa técnica.
Vuex
A próxima grande Vuex
é Vuex
. Possui alguma complexidade projetada por projeto para digitar:
const result: Promise<number> = this.$store.dispatch('action_name', { payload: 1 })
O problema é que 'action_name'
pode não existir, aceitar outros argumentos ou retornar um tipo diferente. Isso não é o que você espera de um aplicativo totalmente digitado.
Quais são as soluções existentes?
classe vuex
vuex-class
é um conjunto de decoradores para permitir acesso fácil dos componentes baseados em classe aos componentes internos do Vuex
.
Porém, não é digitado com segurança, pois não pode interferir nos tipos de estado, getters, mutações e ações.

Obviamente, você pode anotar manualmente tipos de propriedades.

Mas o que você fará quando o tipo real de seu estado, getters, mutações ou ações mudar? Você terá uma incompatibilidade de tipo oculto.
vuex-simple
É aí que o vuex-simple
nos ajuda. Na verdade, ele oferece uma maneira completamente diferente de escrever seu código Vuex
e é isso que o torna seguro. Vamos dar uma olhada:
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 } }
Posteriormente, este módulo digitado pode ser registrado dentro do seu Vuex
forma:
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)
Agora, temos uma instância 100% nativa do Vuex.Store
e todas as informações de tipo incluídas. Para usar esse armazenamento digitado no componente, podemos escrever apenas uma linha 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 }
Agora, Vuex
o Vuex
que pode ser usado com segurança dentro do nosso projeto.
Quando alteramos algo dentro de nossa definição de loja, ele é automaticamente refletido nos componentes que usam essa loja. Se algo falhar - nós sabemos o mais rápido possível.
Também existem bibliotecas diferentes que fazem o mesmo, mas possuem API diferente. Escolha o que melhor lhe convier.
Chamadas de API
Quando o Vuex
configurado corretamente, precisamos preenchê-lo com dados.
Vamos dar uma olhada em nossa definição de ação novamente:
@Action() public async fetchComments (): Promise<CommentType[]> { // Calling some API: const commentsList = await api.fetchComments() // ... return commentsList }
Como podemos saber que ele realmente retornará uma lista de CommentType
e não um único number
ou AuthorType
instâncias de AuthorType
?
Não podemos controlar o servidor. E o servidor pode realmente quebrar o contrato. Ou podemos simplesmente passar a instância da api
errada, digitar o erro na URL ou o que for.
Como podemos estar seguros? Podemos usar a digitação em tempo de execução! Deixe-me apresentar io-ts
para você:
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>
O que fazemos aqui?
- Definimos uma instância de
ts.type
com campos que precisam ser verificados em tempo de execução quando recebermos uma resposta do servidor - Definimos um tipo estático para ser usado na anotação sem nenhum padrão adicional
E depois podemos usá-lo em nossas chamadas de 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) }
Com a ajuda do io-ts-promise
, podemos retornar uma Promise
em um estado com falha se a resposta do servidor não corresponder ao tipo ts.array(Comment)
. Realmente funciona como uma validação.
fetchComments() .then((data) => /* ... */ .catch(/* Happens with both request failure and incorrect response type */)
Além disso, a anotação do tipo de retorno está sincronizada com o método .decode
. E você não pode colocar bobagens aleatórias lá:

Com a combinação de verificações em tempo de execução e estáticas, podemos ter certeza de que nossas solicitações não falharão devido à incompatibilidade de tipos.
Mas, para ter 100% de certeza de que tudo funciona, eu recomendaria o uso de testes baseados em contrato: dê uma olhada no pact
como exemplo. E monitore seu aplicativo com o Sentry
.
Roteador Vue
O próximo problema é que this.$router.push({ name: 'wrong!' })
Não funciona da maneira que queremos.
Eu diria que seria ideal ser avisado pelo compilador de que estamos encaminhando para a direção errada e essa rota não existe.
Mas isso não é possível. E não se pode fazer muito: existem muitas rotas dinâmicas, regex, fallbacks, permissões, etc. que podem eventualmente quebrar. A única opção é testar cada uma this.$router
chamadas this.$router
no seu aplicativo.
vue-test-utils
Falando sobre testes, não tenho desculpas para não mencionar @vue/test-utils
que também tem alguns problemas com a digitação.
Quando tentarmos testar nosso novo componente brilhante com a propriedade typedStore
, descobriremos que realmente não podemos fazer isso de acordo com o typescript
:

Por que isso acontece? Isso acontece porque a chamada mount()
não sabe nada sobre o tipo do seu componente, porque todos os componentes têm um tipo VueConstructor<Vue>
por padrão:

É daí que todos os problemas vêm. O que pode ser feito?
Você pode usar o vuetype
para produzir tipagens YouComponent.vue.d.ts
que informarão seus testes sobre o tipo exato do componente montado.
Você também pode acompanhar esse problema para o progresso.
Mas não gosto dessa ideia. Estes são testes, eles podem falhar. Não é grande coisa.
É por isso que me (wrapper.vm as any).whatever
a (wrapper.vm as any).whatever
. Isso me poupa bastante tempo para escrever testes. Mas estraga um pouco a Experiência do desenvolvedor.
Tome sua própria decisão aqui:
- Use o
vuetype
até o fim - Aplique-o parcialmente aos componentes mais importantes com a maior quantidade de testes e atualize-o regularmente
- Use
any
como substituto
Conclusão
O nível médio de suporte à typescript
no ecossistema Vue
aumentou nos últimos dois anos:
Nuxt
introduziu o nuxt-ts
Nuxt
primeira vez e agora envia o ts
builds por padrãoVue@3
terá suporte aprimorado à typescript
- Mais aplicativos e plugins de terceiros fornecerão definições de tipo
Mas, é a produção pronta no momento. Estas são apenas algumas coisas para melhorar! Escrever código Vue
segurança de tipo realmente melhora sua Experiência do desenvolvedor e permite que você se concentre nas coisas importantes, deixando o trabalho pesado para o compilador.
Quais são os seus hacks e ferramentas favoritos para digitar aplicativos Vue
? Vamos discutir isso na seção de comentários.