Vue.js: TypeScript: Melhores Práticas
Última atualização: 2026-08-26
O TypeScript é o padrão para projetos Vue 3 de nível empresarial — ele oferece segurança de tipos, autocompletar no IDE e confiança na refatoração. O <script setup lang="ts"> no Vue 3.4+ leva a integração com o TypeScript a novos patamares: inferência automática de defineProps e defineEmits, além de tipos de referência de componentes perfeitos.
Dominar o Vue e o TypeScript é a chave para passar do nível iniciante para o avançado. Este curso vai te ajudar a construir uma base de conhecimento abrangente sobre o Vue 3 e o TypeScript.
1. O que você vai aprender
<script setup lang="ts">Noções básicas- 5 maneiras de escrever
definePropsusando genéricos - definir o tipo
emits+ restrições de parâmetros - Inferência de tipos ref / reativa / computada
- Tipo de referência do componente (useTemplateRef)
- Configurações avançadas do Volar
- Melhores práticas para o arquivo tsconfig.json
- 5 erros comuns no TS
2. O pesadelo do “undefined is not a function” em um projeto em JavaScript
(1) Problema: 100 erros “indefinidos” em um projeto de JavaScript
O painel de administração do Alice era originalmente em JS. Erros comuns:
// ❌ The "Broken" Version:JS Errors are not detected until runtime
export default {
props: {
user: { type: Object, required: true }
// Misspelled prop name: userName (No errors)
// prop Wrong type:No errors
// emit The event name is incorrect:No errors
}
}
<!-- The parent component uses userName,However, the child component defines user -->
<UserCard userName="Alice" /> <!-- ❌ I didn't realize it until runtime -->
<!-- emit The event name is incorrect -->
<Child @updae="handler" /> <!-- ❌ I didn't realize it until runtime -->
Mais de 100 possíveis erros são revelados apenas durante a execução, o que resulta em altos custos de depuração.
(2) Solução com Vue 3 + TypeScript
<!-- Child component:UserCard.vue -->
<script setup lang="ts">
interface User {
id: number
name: string
email: string
}
const props = defineProps<{
user: User
variant?: 'primary' | 'secondary'
}>()
const emit = defineEmits<{
select: [userId: number]
delete: [userId: number]
}>()
</script>
<!-- Using Parent Components:An error occurs during compilation -->
<UserCard :user="alice" /> <!-- ✅ Compile-Time Type Checking -->
<UserCard @updae="handler" /> <!-- ❌ TS Error: The event does not exist. -->
Todos os erros são detectados na hora da compilação, e o IDE os destaca em vermelho.
(3) Receita
Depois de adicionar o TypeScript:
- Erros de execução: 80% → 10% (interceptados na fase de compilação)
- Autocompletar do IDE: Precisão de 95% ou mais
- Recuperando a confiança: Segurança de tipos — sem medo de causar erros
- Colaboração em equipe: interfaces claras e baixos custos de comunicação
3. Configuração básica do TypeScript
(1) configuração do script lang="ts"
<template>
<p>{{ count }}</p>
<button @click="increment">+</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// ✅ TS Automatic Inference:Ref<number>
const count = ref(0)
// ✅ Parameters and Return Types
function increment(): void {
count.value++
}
</script>
(2) Noções básicas sobre o tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
]
}
(3) As 5 principais configurações recomendadas
{
"compilerOptions": {
// 1. Strict Mode(Guaranteed to Open)
"strict": true,
// 2. No implicit any
"noImplicitAny": true,
// 3. Strict Null Checks
"strictNullChecks": true,
// 4. Strict Function Types
"strictFunctionTypes": true,
// 5. Strictly Bounded Calls
"strictBindCallApply": true
}
}
4. 5 maneiras de escrever defineProps com genéricos
(1) Método 1: Tipos primitivos
const props = defineProps<{
name: string
age: number
active: boolean
}>()
(2) Método 2: Opcional + Valor padrão
// withDefaults Provide a default value
const props = withDefaults(defineProps<{
name: string
age?: number
variant?: 'primary' | 'secondary'
}>(), {
age: 18,
variant: 'primary'
})
(3) Abordagem 3: Objetos complexos / matrizes
interface User {
id: number
name: string
email: string
}
const props = defineProps<{
user: User
items: User[]
config: Record<string, unknown>
}>()
(4) Abordagem 4: A função prop
const props = defineProps<{
formatter: (value: number) => string
onChange: (value: string) => void
}>()
(5) Abordagem 5: Componentes genéricos
// Generic Components:List<T> Can be specified item Type
<script setup lang="ts" generic="T extends { id: number }">
defineProps<{
items: T[]
selected?: T
}>()
</script>
<!-- Usage -->
<List :items="users" /> <!-- T = User -->
<List :items="products" /> <!-- T = Product -->
5. O tipo defineEmits
(1) 5 tipos de declarações de evento
// 1. Simple Events
const emit = defineEmits<{
click: []
submit: []
}>()
// 2. Single parameter
const emit = defineEmits<{
select: [id: number]
delete: [id: number]
}>()
// 3. Multi-parameter
const emit = defineEmits<{
change: [id: number, oldValue: string, newValue: string]
}>()
// 4. Optional Parameters
const emit = defineEmits<{
search: [query?: string]
load: [id: number, options?: object]
}>()
// 5. void Return Value
const emit = defineEmits<{
success: [data: object]
error: [message: string]
}>()
(2) Eventos desencadeadores
const emit = defineEmits<{
select: [id: number]
delete: [id: number]
}>()
// ✅ TypeScript Check Parameter Types
emit('select', 123) // ✅ OK
emit('select', 'abc') // ❌ TS Error
emit('delete', 456)
(3) Como usar o componente pai
<template>
<!-- ✅ TS Check Event Name -->
<Child @select="handleSelect" @delete="handleDelete" />
<!-- ❌ TS Error:The event does not exist. -->
<!-- <Child @updae="handler" /> -->
</template>
<script setup lang="ts">
function handleSelect(id: number) {
console.log('Selected:', id)
}
</script>
6. Tipos ref / reativos / computados
(1) Inferência de tipos para ref
import { ref } from 'vue'
// Automatically inferred as Ref<number>
const count = ref(0)
count.value = 1 // ✅
// Inferred as Ref<string>
const name = ref('Alice')
// Inferred as Ref<number | undefined>(It could be undefined)
const maybeNumber = ref<number>()
maybeNumber.value // type: number | undefined
(2) Inferência de tipos reativa
import { reactive } from 'vue'
// Automatic Inference
const state = reactive({
count: 0,
user: { name: 'Alice', age: 25 }
})
state.count // type: number
state.user.name // type: string
// Explicit Type
interface State {
count: number
items: string[]
}
const s = reactive<State>({
count: 0,
items: []
})
(3) Tipos calculados
import { ref, computed } from 'vue'
const count = ref(10)
// Automatic Inference:ComputedRef<number>
const double = computed(() => count.value * 2)
// Explicit Type
const formatted = computed<string>(() => `Count: ${count.value}`)
7. Tipos de referência de componentes (useTemplateRef)
(1) Visualização 3.5+ com useTemplateRef
<template>
<input ref="usernameInput">
<MyChart ref="chartComponent" :data="chartData" />
</template>
<script setup lang="ts">
import { useTemplateRef, onMounted } from 'vue'
import MyChart from './MyChart.vue'
// ✅ TS Automatic Inference:Ref<HTMLInputElement | null>
const inputRef = useTemplateRef<HTMLInputElement>('usernameInput')
// ✅ Component Instance Types
const chartRef = useTemplateRef<InstanceType<typeof MyChart>>('chartComponent')
onMounted(() => {
inputRef.value?.focus() // TS Auto-Complete
chartRef.value?.refresh()
})
</script>
(2) Vue 3.4 — Sintaxe antiga
// Old notation:Must be done manually ref<>
import { ref, onMounted } from 'vue'
import MyChart from './MyChart.vue'
const inputRef = ref<HTMLInputElement | null>(null)
const chartRef = ref<InstanceType<typeof MyChart> | null>(null)
8. Configuração avançada do Volar
(1) Instalação
# VS Code Install "Vue - Official" Extensions(Volar)
# Search:Vue - Official
(2) Arquivo settings.json recomendado
{
"vue.enabled.volar": true,
"vue.compilerOptions.target": 3.4,
"vue.complete.casing.tags": ["PascalCase", "snake_case"],
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.preferences.includePackageJsonAutoImports": "on",
"editor.formatOnSave": true,
"[vue]": {
"editor.defaultFormatter": "Vue.volar"
}
}
(3) 5 principais características volares
| Recurso | Descrição |
|---|---|
| Inferência de tipos | Inferência perfeita para props, emits e refs |
| Autocompletar | Sugestões inteligentes para nomes de componentes, propriedades e emissões |
| Verificação de erros | Erros exibidos na hora da compilação (por exemplo, tipo de propriedade incorreto) |
| Ir para a definição | F12: Ir para a definição do componente |
| Suporte à refatoração | Renomeie uma propriedade para sincronizar automaticamente todas as referências |
9. Complete Examples: 5 Major TS Patterns
▶ Exemplo: 1. 5 maneiras de escrever defineProps
// 1. Basics
defineProps<{ name: string }>()
// 2. Optional+Default
withDefaults(defineProps<{ name?: string }>(), { name: 'Guest' })
// 3. Complex
defineProps<{ user: User; items: User[] }>()
// 4. Function
defineProps<{ onClick: () => void }>()
// 5. Generics
defineProps<{ items: T[] }>() // Needs generic="T"
▶ Exemplo: 2. defineEmits emite 5 eventos
// 1. Simple
defineEmits<{ click: [] }>()
// 2. Single Parameter
defineEmits<{ select: [id: number] }>()
// 3. More information
defineEmits<{ change: [old: string, new: string] }>()
// 4. Optional
defineEmits<{ search: [q?: string] }>()
// 5. void
defineEmits<{ done: [] }>()
▶ Exemplo: 3. 5 erros comuns em TS
// 1. Property 'x' does not exist
// → Check Spelling,or add a type
// 2. Argument of type 'X' is not assignable
// → Type mismatch,Check Parameter Types
// 3. Type 'X' is not assignable to type 'Y | null'
// → Strict Null Checks, Add ! or ?
// 4. Cannot find module './X'
// → Path error,Check Import
// 5. Object is possibly 'undefined'
// → Optional Chain ?.
▶ Exemplo: 4. 5 Principais comparações de desempenho
| Padrão | Segurança de tipos | Desempenho | Aplicabilidade |
|---|---|---|---|
| JS | ❌ | ⭐⭐⭐⭐⭐ | Protótipo/Pequeno projeto |
| TS (Básico) | ⭐⭐⭐ | ⭐⭐⭐⭐ | Geral |
| TS (Rigoroso) | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Projetos corporativos |
| TS + Volar | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Recomendado |
| TS + tsc build | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | Projetos de grande escala |
▶ Exemplo: 5. 5 Principais estruturas de projetos em Vue 3 + TS
src/
├-- components/ # Public Components
│ ├-- UserCard.vue # <script setup lang="ts">
│ └-- BaseButton.vue
├-- views/ # Page Components
├-- stores/ # Pinia(TS)
├-- composables/ # Composables
├-- types/ # Type Definitions
│ ├-- user.ts # export interface User
│ ├-- api.ts # export interface ApiResponse<T>
│ └-- index.ts # Batch Export
├-- utils/ # Utility Functions
├-- router/ # Routing(TS)
├-- App.vue
└-- main.ts # Entrance
▶ Exemplo: 6. 5 Referência rápida para erros comuns
| Erro | Sintoma | Solução |
|---|---|---|
| Nome da propriedade com erro ortográfico | Erro de compilação | Use o IDE para acessar a definição |
| Erro ortográfico na palavra “emit” | Erro de compilação | Use o tipo “defineEmits” |
| Falha na conversão de tipo | Erro de compilação | Use uma asserção de tipo ou genéricos |
| ref.value indefinido | Tempo de execução | Encadeamento opcional ?. |
| Referência circular de tipo | Erro de compilação | Use uma importação apenas de tipo |
❓ Perguntas Frequentes
P: O Vue 3 + TS tem desempenho inferior ao JS? R: A verificação de tipos ocorre em tempo de compilação e não tem impacto em tempo de execução (os tipos são eliminados). O Volar compila muito rapidamente, então a diferença é quase imperceptível.
P: Genérico
definePropsx declaração em tempo de execução? R: Recomendamos o uso de genéricos. O TypeScript infere automaticamente os tipos das propriedades, e os IDEs oferecem um excelente autocompletamento. As declarações em tempo de execução exigem que você escreva validadores manualmente.
P: Como se escrevem componentes genéricos? R:
<script setup lang="ts" generic="T">, depoisdefineProps<{ items: T[] }>(). Compatível com o Vue 3.3 e versões posteriores.
P: Como se escreve
defineModelem TypeScript? R:const modelValue = defineModel<string>('modelValue', { default: '' }). Especifique o tipo usando genéricos.
P: O
useTemplateRefrequer o Vue 3.5 ou uma versão posterior? R: Sim. Para o Vue 3.4 e versões anteriores, use oref<HTMLInputElement | null>(null).
P: É necessário ativar o modo estrito no tsconfig.json? R: É obrigatório para projetos corporativos. Iniciantes podem começar desativando o modo estrito e ativá-lo gradualmente. Recomendado:
"strict": true, "noImplicitAny": true, "strictNullChecks": true.
P: O que é o Volcano / vue-tsc? R: O vue-tsc é uma ferramenta de verificação de tipos em TypeScript para projetos Vue (desenvolvida pela equipe Volar).
npx vue-tsc --noEmitRealiza verificações de tipos (não gera código JS, apenas verifica os tipos).
P: É possível usar o Vue 3 com TypeScript no SSR? R: Sim. O Nuxt 3 oferece suporte integrado ao TypeScript. Tanto o Pinia quanto o Vue Router oferecem suporte nativo ao TypeScript.
📖 Resumo
<script setup lang="ts">é a configuração padrão para o Vue 3 + TS- 5 maneiras de escrever
definePropscom genéricos: básico / opcional / complexo / função / genérico - O tipo
defineEmitse as restrições de parâmetros garantem que os eventos sejam válidos - 5 Recomendações: tsconfig:strict / noImplicitAny / strictNullChecks etc.
- useTemplateRef (Vue 3.5+) Inferência de tipos perfeita
- O Volar é uma extensão indispensável para o VS Code
- 5 erros comuns: ortografia / tipos / referências circulares
📝 Exercícios
-
Questões básicas (Dificuldade: ⭐)
Converter 1 componente de JS para TS:
<script setup lang="ts">- defineProps Genéricos
- tipo defineEmits
- A compilação e a verificação foram bem-sucedidas
-
Problemas avançados (Dificuldade: ⭐⭐)
Implementação de um sistema de tipos TS completo:
- 5 interfaces (Usuário / Produto / Pedido / Categoria / Carrinho)
- 5 componentes que utilizam
definePropscom genéricos - 5 componentes do tipo
defineEmits - modo estrito do tsconfig
- Configuração do Volar
-
Problema de desafio (Dificuldade: ⭐⭐⭐)
Implementação de um projeto completo “Vue 3 + TS”:
- 5 lojas (Pinia + TS)
- 5 composables (TS)
- 10 Componentes (Genéricos + Inferência de Tipos)
- Soluções para 5 erros comuns do TS
- Verificação de tipos do vue-tsc (CI/CD)
- Volar + IDE: a configuração perfeita
- Geração automática de documentação de tipos (typedoc)