Шпаргалка по TypeScript для разработчиков Vue 3

✅ Основы типов

Простейшие аннотации, которые помогают избежать ошибок при работе с переменными и данными из API.

let count: number = 0
let title: string = "Hello"
let isActive: boolean = true
let tags: string[] = ["vue", "ts"]
let obj: { id: number; name: string } = { id: 1, name: "Vue" }

Union и literal типы позволяют ограничивать состояние или входные данные.

let state: "loading" | "error" | "success"
let value: number | null

Типы для функций дают уверенность в возвращаемом результате.

function add(a: number, b: number): number {
  return a + b
}

✅ Type alias и interface

Это базовые инструменты описания структуры данных.
type — универсальнее, interface — лучше для объектов

type ID = string | number
type ClickHandler = (e: MouseEvent) => void

interface User {
  id: ID
  name: string
  email?: string // необязательно, если может отсутствовать
}

Интерфейсы легко расширяются:

interface Admin extends User {
  role: "admin"
}

✅ Enum и const Enum

Enums — удобная альтернатива строковым литералам, особенно если значения используются многократно.

enum Status {
  Active,
  Disabled
}

const enum Dir {
  Up = "UP",
  Down = "DOWN"
}

const enum оптимальнее по размеру бандла, но требует поддержки в сборке.

✅ Generics (обобщения)

Нужны, когда тип должен зависеть от входных данных — например, в хукаx, компонентах и сервисах.

function wrap<T>(value: T): T[] {
  return [value]
}
const users = wrap<User>({ id: 1, name: "John" })

Generics активно используются под капотом Vue (ref<T>, computed<T>).

✅ Utility Types (очень часто в Vue-продакшене)

Ускоряют типизацию и делают код гибче.

Partial<T>Делает все поля optional

Required<T>Все обязательные

Readonly<T>Запрещает изменять

Pick<T, K>Берёт только указанные поля

Omit<T, K>Удаляет указанные поля

Record<K, T>Типизированные ключи и значения

Пример:

type UserPreview = Pick<User, "id" | "name">

✅ Narrowing (сужение типов)

Обязательный навык при обработке данных из API, где часто приходят unknown | null.

function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase())
  } else {
    console.log(id.toFixed(2))
  }
}

Если TS “сомневается” — проверяй!

✅ Работа с API и Axios

Типизация ответа делает код надёжным и автокомплит более умным.

interface Todo {
  id: number
  title: string
}

async function getTodos(): Promise<Todo[]> {
  const { data } = await axios.get<Todo[]>("/api/todos")
  return data
}

✅ Typing props (Composition API)

Типизация пропов избавляет от багов, связанных с неверными входными данными.

const props = defineProps<{
  title: string
  count?: number
}>()

✅ Typing emits

Позволяет контролировать события: имена и типы передаваемых данных.

const emit = defineEmits<{
  (e: "update", value: number): void
}>()
emit("update", 10)

✅ Typing refs и reactive

TS помогает избежать перепутывания типов .value и обычных переменных.

const count = ref<number>(0)
const user = reactive<User>({
  id: 1,
  name: "John"
})

✅ Computed

Явная типизация нужна, когда TS не может вывести тип сам.

const double = computed<number>(() => count.value * 2)

✅ Typing events & templates

Мышь — MouseEvent, клавиатура — KeyboardEvent, формы — SubmitEvent.

function handleClick(e: MouseEvent) {
  console.log(e.clientX)
}

Шаблон:

<button @click="handleClick">Click</button>

✅ Vue Router с типами

Router не всегда знает типы params, поэтому иногда нужен каст:

import { useRoute } from "vue-router"

const route = useRoute()
const id = route.params.id as string

✅ Pinia Store: типизация состояния и действий

TS позволяет точно описать значение state и сигнатуру методов.

export const useUserStore = defineStore("user", {
  state: () => ({
    user: null as User | null
  }),
  actions: {
    setUser(u: User) {
      this.user = u
    }
  }
})