Skip to content

TypeScript com a API de Opções ​

Esta página presume que já leste a visão geral no Utilizando a Vue com a TypeScript.

DICA

Embora a Vue suporte o uso da TypeScript com a API de Opções, é recomendado usar a Vue com a TypeScript através da API de Composição visto que oferece a mais simples, mais eficiente e mais robusta inferência de tipo.

Tipos para as Propriedades do Componente ​

A inferência de tipo para as propriedades na API de Opções exige o envolvimento do componente com defineComponent(). Com isto, a Vue é capaz de inferir os tipos para as propriedades baseada na opção props, levando opções adicionais tais como required: true e default em conta:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  // inferência de tipo ativada
  props: {
    name: String,
    id: [Number, String],
    msg: { type: String, required: true },
    metadata: null
  },
  mounted() {
    this.name // type: string | undefined
    this.id // type: number | string | undefined
    this.msg // type: string
    this.metadata // type: any
  }
})

No entanto, as opções props de tempo de execução apenas suportam a utilização de funções construtoras como um tipo da propriedade - não existe maneira de especificar tipos complexos tais como objetos com propriedades encaixadas ou assinaturas de chamada de função.

Para anotar tipos de propriedades complexas, podemos utilizar o tipo utilitário PropType:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  author: string
  year: number
}

export default defineComponent({
  props: {
    book: {
      // fornece tipo mais específico para `Object`
      type: Object as PropType<Book>,
      required: true
    },
    // também pode anotar funções
    callback: Function as PropType<(id: number) => void>
  },
  mounted() {
    this.book.title // string
    this.book.year // number

    // Erro de TS: argumento de tipo 'string' não é
    // atribuível ao parâmetro de tipo 'number'
    this.callback?.('123')
  }
})

Advertências ​

Se a tua versão de TypeScript for menor do que 4.7, tens que ser cuidadoso quando estiveres a usar os valores de função para as opções das propriedades validator e default - certifica-te de que utilizar funções em flecha:

ts
import { defineComponent } from 'vue'
import type { PropType } from 'vue'

interface Book {
  title: string
  year?: number
}

export default defineComponent({
  props: {
    bookA: {
      type: Object as PropType<Book>,
      // Certifica-te de que utilizas funções em flecha
      default: () => ({
        title: 'Arrow Function Expression'
      }),
      validator: (book: Book) => !!book.title
    }
  }
})

Isto impedi a TypeScript de ter que inferir o tipo do this dentro destas funções, o que, infelizmente, pode causar a inferência de tipo falhar na realização do seu trabalho. Isto era uma anterior limitação do desenho, e agora foi melhorada na TypeScript 4.7.

Tipos para as Emissões do Componente ​

Nós podemos declarar o tipo da carga esperada para um evento emitido utilizando a sintaxe de objeto da opção emits. Além disto, todos os eventos emitidos não declarados lançarão um erro de tipo quando chamados:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  emits: {
    addBook(payload: { bookName: string }) {
      // realiza a validação em tempo de execução
      return payload.bookName.length > 0
    }
  },
  methods: {
    onSubmit() {
      this.$emit('addBook', {
        bookName: 123 // Erro de tipo!
      })

      this.$emit('non-declared-event') // Erro de tipo!
    }
  }
})

Tipos para as Propriedades Computadas ​

Uma propriedade computada infere o seu tipo baseado no seu valor de retorno:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Hello!'
    }
  },
  computed: {
    greeting() {
      return this.message + '!'
    }
  },
  mounted() {
    this.greeting // type: string
  }
})

Em alguns casos, podes querer explicitamente anotar o tipo de uma propriedade computada para garantir que sua implementação esteja correta:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  data() {
    return {
      message: 'Hello!'
    }
  },
  computed: {
    // anotar explicitamente o tipo de retorno
    greeting(): string {
      return this.message + '!'
    },

    // anotando uma propriedade computada gravável
    greetingUppercased: {
      get(): string {
        return this.greeting.toUpperCase()
      },
      set(newValue: string) {
        this.message = newValue.toUpperCase()
      }
    }
  }
})

As anotações explícitas também podem ser exigidas em alguns casos extremos onde a TypeScript falha em inferir o tipo de uma propriedade computada por causa de laços de inferência circular.

Tipos para os Manipuladores de Evento ​

Quando estiveres lidando com eventos de DOM nativos, pode ser útil definir um tipo para o argumento que passamos para o manipulador corretamente. Vamos dar uma vista de olhos neste exemplo:

vue
<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  methods: {
    handleChange(event) {
      // `event` tem implicitamente o tipo `any`
      console.log(event.target.value)
    }
  }
})
</script>

<template>
  <input type="text" @change="handleChange" />
</template>

Sem a anotação de tipo, o argumento event terá implicitamente um tipo de any. Isto também resultará em um erro de TypeScript se "strict": true ou "noImplicitAny": true forem utilizadas no tsconfig.json. É portanto recomendado anotar explicitamente o argumento dos manipuladores de eventos. Além disto, podes precisar de explicitamente usar as asserções de tipo quando estiveres a acessar as propriedades do event:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  methods: {
    handleChange(event: Event) {
      console.log((event.target as HTMLInputElement).value)
    }
  }
})

Aumentando as Propriedades Globais ​

Algumas extensões são instaladas globalmente e suas propriedades estão disponíveis para todas instâncias de componente através de app.config.globalProperties. Por exemplo, podemos instalar this.$http para requisição de dados ou this.$translate para internacionalização. Para fazer isto funcionar bem com a TypeScript, a Vue expõe uma interface ComponentCustomProperties desenhada para ser aumentada através da aumentação de módulo de TypeScript:

ts
import axios from 'axios'

declare module 'vue' {
  interface ComponentCustomProperties {
    $http: typeof axios
    $translate: (key: string) => string
  }
}

Consulte também:

Colocação de Aumentação de Tipo ​

Nós podemos colocar esta aumentação de tipo em um ficheiro .ts, ou em um ficheiro *.d.ts de largura de projeto. De um modo ou outro, certifica-te de que é incluída no tsconfig.json. Para autores de extensão ou biblioteca, este ficheiro deve ser especificado na propriedade types no package.json.

Para tirar vantagem da aumentação de módulo, precisarás garantir que a aumentação é colocada em um módulo de TypeScript. Isto é, o ficheiro precisa conter ao menos um import ou export de alto nível, mesmo se for apenas export {}. Se a aumentação for colocada fora de um módulo, ela sobrescreverá os tipos originais no lugar de aumentá-los!

ts
// Não funciona, sobrescreve os tipos originais.
declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}
ts
// Funciona corretamente
export {}

declare module 'vue' {
  interface ComponentCustomProperties {
    $translate: (key: string) => string
  }
}

Aumentando Opções Personalizadas ​

Algumas extensões, por exemplo vue-router, fornecem suporta para opções de componente personalizadas tais como beforeRouterEnter:

ts
import { defineComponent } from 'vue'

export default defineComponent({
  beforeRouteEnter(to, from, next) {
    // ...
  }
})

Sem a aumentação de tipo adequada, os argumentos deste gatilho terão implicitamente o tipo any. Nós podemos aumentar a interface ComponentCustomOptions para suportar estas opções personalizadas:

ts
import { Route } from 'vue-router'

declare module 'vue' {
  interface ComponentCustomOptions {
    beforeRouteEnter?(to: Route, from: Route, next: () => void): void
  }
}

Agora a opção beforeRouteEnter terá o seu tipo apropriadamente definido. Nota que isto é apenas um exemplo - bibliotecas com os tipos corretamente definidos como vue-router devem automaticamente realizar estas aumentações em suas próprias definições de tipo.

A colocação desta aumentação está sujeita as mesmas restrições que as aumentações de propriedade globais.

Consulte também:

TypeScript com a API de Opções has loaded