Avatar ·

Ошибка "Property does not exist on type 'never'" в TypeScript при работе с Vue.js

Привет! Я новичок в TypeScript и использую его вместе с Vue.js 3. У меня возникла проблема, которую я не могу решить уже несколько часов. У меня есть компонент, который получает массив объектов через пропсы и должен отображать их в списке. Но когда я пытаюсь обратиться к свойству объекта внутри массива, TypeScript выдает ошибку: "Property 'name' does not exist on type 'never'". Вот упрощенный код моего компонента: ```typescript // ParentComponent.vue ``` ```typescript // ChildComponent.vue ``` Ошибка возникает именно на `item.name` — TypeScript говорит, что у `never` нет свойства `name`. Я пробовал указывать тип пропса как `Array<{id: number, name: string}>`, но это не помогает. В чем проблема и как её исправить? Заранее спасибо! <<>>

Причина ошибки

Ошибка "Property does not exist on type 'never'" возникает из-за того, что TypeScript не может вывести тип элементов массива, когда массив объявлен пустым (ref([])) или когда пропс объявлен без явной типизации. В таких случаях TypeScript выводит тип массива как never[], а элемент соответственно как never — тип, который не имеет ни одного значения и, следовательно, никаких свойств.

Решение: правильная типизация

Нужно явно указать тип для массива на всех уровнях: при создании реактивной переменной, при объявлении пропсов и, опционально, в шаблоне.

Шаг 1: Типизируйте ref

// ParentComponent.vue
import { ref } from 'vue'

interface Item {
  id: number
  name: string
}

const items = ref<Item[]>([]) // Явно указываем тип массива

Теперь TypeScript знает, что items — это массив объектов с полями id и name.

Шаг 2: Типизируйте пропсы в дочернем компоненте

Лучший способ — использовать defineProps с дженериком:

// ChildComponent.vue
<script setup lang="ts">
interface Item {
  id: number
  name: string
}

const props = defineProps<{
  items: Item[]
}>()
</script>

Если вы предпочитаете старый синтаксис с объектом, укажите тип через as PropType:

import { PropType } from 'vue'

defineProps({
  items: {
    type: Array as PropType<Item[]>,
    required: true
  }
})

Шаг 3: (Опционально) Используйте дженерик для v-for

В некоторых сложных случаях может потребоваться явно указать тип в шаблоне:

<li v-for="(item: Item) in items" :key="item.id">
  {{ item.name }}
</li>

Почему это работает?

  • Явная типизация ref — задает тип для реактивной переменной, даже когда она пуста.
  • Типизация пропсов — сообщает TypeScript, какие данные ожидает компонент.
  • PropType — решает проблему, когда обычный type: Array не передает TypeScript информацию о вложенных типах.

Полный исправленный код

// ParentComponent.vue
<template>
  <ChildComponent :items="items" />
</template>

<script setup lang="ts">
import { ref } from 'vue'
import ChildComponent from './ChildComponent.vue'

interface Item {
  id: number
  name: string
}

const items = ref<Item[]>([])
// items.value = await fetchItemsFromAPI()
</script>

// ChildComponent.vue
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      {{ item.name }}
    </li>
  </ul>
</template>

<script setup lang="ts">
interface Item {
  id: number
  name: string
}

const props = defineProps<{
  items: Item[]
}>()
</script>
<<>>

Альтернативный подход: использование утверждения типа (type assertion)

Если вы не хотите менять объявление пропсов или ref (например, в унаследованном коде), можно использовать утверждение типа прямо в шаблоне или в вычисляемом свойстве:

// В шаблоне
<li v-for="item in (items as Array<{id: number, name: string}>)" :key="item.id">
  {{ item.name }}
</li>

// Или через computed
import { computed } from 'vue'

const typedItems = computed(() => items as Array<{id: number, name: string}>)

Этот подход — "быстрый фикс", но он менее безопасен, так как обходит проверку типов. Рекомендую использовать его только для быстрого прототипирования или исправления legacy-кода. В продакшене всегда лучше явно типизировать данные.

Еще один вариант: дженерики в defineProps

В Vue 3.3+ появилась возможность использовать дженерики напрямую:

<script setup lang="ts" generic="T extends { id: number, name: string }">
defineProps<{
  items: T[]
}>()
</script>

Это дает гибкость, если тип элементов может меняться, но сохраняет типизацию.