Создаём
Разработка защищённой B2B-платформы для легального музыкального стриминга в кафе, отелях и ТРЦ
Audiomix
Создание лендинга и аналитики поведения пользователей для бренда натуральных средств от бессонницы и аллергии
SomniaCare
Цифровая экосистема для помощи профессиональным спортсменам в построении второй карьеры после завершения карьеры
NextPlay
Корпоративный сайт и автоматизация заявок через n8n для ведущего интегратора бизнес-решений на базе SAP
IntegraPro
Разработка защищённой B2B-платформы для легального музыкального стриминга в кафе, отелях и ТРЦ
Audiomix
Создание лендинга и аналитики поведения пользователей для бренда натуральных средств от бессонницы и аллергии
SomniaCare
Цифровая экосистема для помощи профессиональным спортсменам в построении второй карьеры после завершения карьеры
NextPlay
Корпоративный сайт и автоматизация заявок через n8n для ведущего интегратора бизнес-решений на базе SAP
IntegraPro
Главная/Блог/TypeScript Strict Mode: Практика использования в enterprise Nuxt проектах
Разработка2025-11-1014 мин

TypeScript Strict Mode: Практика использования в enterprise Nuxt проектах

Внедряем строгую типизацию для повышения надёжности, maintainability и Developer Experience

Команда ExtTeam
Команда ExtTeam
Full-stack разработчики
TypeScript Strict Mode: Практика использования в enterprise Nuxt проектах

Введение

TypeScript strict mode включает набор опций компилятора, которые обеспечивают максимальную type safety. Хотя это требует больше усилий при написании кода, преимущества в долгосрочной перспективе огромны.

В ExtTeam мы используем TypeScript strict mode во всех enterprise проектах на Nuxt, что позволяет отлавливать баги на этапе компиляции, улучшать refactoring опыт и повышать качество кода.

Что включает Strict Mode?

Включение strict: true в tsconfig.json активирует следующие опции:

  • noImplicitAny — запрещает неявный any тип
  • strictNullChecks — null и undefined как отдельные типы
  • strictFunctionTypes — строгая проверка типов функций
  • strictBindCallApply — строгая типизация bind, call, apply
  • strictPropertyInitialization — обязательная инициализация свойств класса
  • noImplicitThis — запрет неявного this
  • alwaysStrict — добавление "use strict" в каждый файл

Настройка в Nuxt 4

Nuxt 4 поддерживает strict mode из коробки:

typescript
// nuxt.config.ts
export default defineNuxtConfig({
  typescript: {
    strict: true,
    typeCheck: true // Проверка типов при сборке
  }
})

Конфигурация tsconfig.json:

json
{
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true
  }
}

Типизация Composables

Composables должны быть полностью типизированы:

typescript
// composables/useUser.ts
import type { Ref } from 'vue'

interface User {
  id: string
  name: string
  email: string
  role: 'admin' | 'user' | 'guest'
}

interface UseUserReturn {
  user: Ref<User | null>
  isAuthenticated: Ref<boolean>
  login: (email: string, password: string) => Promise<void>
  logout: () => Promise<void>
}

export const useUser = (): UseUserReturn => {
  const user = ref<User | null>(null)
  const isAuthenticated = computed(() => user.value !== null)

  const login = async (email: string, password: string): Promise<void> => {
    const response = await $fetch<User>('/api/auth/login', {
      method: 'POST',
      body: { email, password }
    })
    user.value = response
  }

  const logout = async (): Promise<void> => {
    await $fetch('/api/auth/logout', { method: 'POST' })
    user.value = null
  }

  return {
    user: readonly(user),
    isAuthenticated: readonly(isAuthenticated),
    login,
    logout
  }
}

Типизация Pages

Страницы Nuxt должны типизировать props и data:

typescript
// pages/projects/[slug].vue
<script setup lang="ts">
import type { DetailedProject } from '~/types/ui'

interface Props {
  slug: string
}

const props = defineProps<Props>()

// useFetch с типизацией
const { data: project, error } = await useFetch<DetailedProject>(
  `/api/projects/${props.slug}`,
  {
    key: `project-${props.slug}`
  }
)

// Проверка на null (strictNullChecks)
if (!project.value) {
  throw createError({
    statusCode: 404,
    statusMessage: 'Project not found'
  })
}
</script>

Type-safe API Calls

Все API вызовы должны быть типизированы:

typescript
// types/api.ts
export interface ApiResponse<T> {
  data: T
  status: 'success' | 'error'
  message?: string
}

export interface CreateProjectRequest {
  title: string
  description: string
  tags: string[]
}

export interface Project {
  id: string
  title: string
  slug: string
  description: string
  createdAt: string
}

// utils/api.ts
export const api = {
  projects: {
    create: (data: CreateProjectRequest) =>
      $fetch<ApiResponse<Project>>('/api/projects', {
        method: 'POST',
        body: data
      }),

    getById: (id: string) =>
      $fetch<ApiResponse<Project>>(`/api/projects/${id}`),

    list: (params?: { page?: number; limit?: number }) =>
      $fetch<ApiResponse<Project[]>>('/api/projects', {
        params
      })
  }
}

Миграция на Strict Mode

Постепенная миграция legacy проекта на strict mode:

  • Включите strict: false, но добавьте отдельные опции по одной
  • Начните с noImplicitAny — самая полезная опция
  • Исправьте ошибки в shared/types и utils
  • Затем включите strictNullChecks
  • Постепенно мигрируйте composables и components
  • Используйте @ts-ignore только как временное решение
  • После полной миграции включите strict: true

Пример поэтапной миграции tsconfig.json:

json
// Этап 1: noImplicitAny
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true
  }
}

// Этап 2: + strictNullChecks
{
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

// Финал: full strict
{
  "compilerOptions": {
    "strict": true
  }
}

Общие проблемы и решения

Проблема: Optional chaining и nullish coalescing

typescript
// ❌ Плохо: может быть null
const name = user.name

// ✅ Хорошо: безопасный доступ
const name = user?.name ?? 'Guest'

// ✅ Type guard
if (user?.name) {
  console.log(user.name.toUpperCase())
}

Проблема: Type narrowing для union types

typescript
type Status = 'loading' | 'success' | 'error'

// ❌ Ошибка: Property 'data' does not exist
function handleStatus(status: Status, data: any) {
  if (status === 'success') {
    return data.value
  }
}

// ✅ Правильно: Type narrowing
type LoadingState = { status: 'loading' }
type SuccessState = { status: 'success'; data: any }
type ErrorState = { status: 'error'; error: string }

type State = LoadingState | SuccessState | ErrorState

function handleState(state: State) {
  if (state.status === 'success') {
    return state.data // TypeScript знает, что это SuccessState
  }
}

Best Practices

  • Создавайте типы в централизованных файлах (types/)
  • Используйте interface для объектов, type для unions/intersections
  • Предпочитайте readonly для immutable данных
  • Используйте Utility Types (Partial, Pick, Omit, Record)
  • Избегайте any — используйте unknown для неизвестных типов
  • Применяйте Type Guards для сужения типов
  • Документируйте сложные типы JSDoc комментариями
  • Используйте satisfies оператор для проверки соответствия типу

Performance Implications

Strict mode не влияет на runtime производительность — весь TypeScript код компилируется в обычный JavaScript. Однако:

  • Время компиляции может увеличиться на 10-20%
  • typeCheck: true в nuxt.config замедляет dev сборку
  • Используйте vue-tsc --noEmit для проверки типов отдельно
  • В production strict mode не влияет на скорость выполнения

Заключение

TypeScript strict mode — must-have для enterprise Nuxt проектов. Хотя начальные затраты времени выше, long-term выгоды в виде меньшего количества багов, лучшего refactoring опыта и улучшенной документации кода полностью их оправдывают.

В ExtTeam мы не представляем разработку без strict mode — это наш стандарт качества для всех новых проектов.

Теги

TypeScriptNuxt 4Type SafetyBest PracticesDX