Vue.js 3: проверка типа реквизита с помощью пользовательского типа

#typescript #vue.js #vue-component #vuejs3

#typescript #vue.js #vue-компонент #vuejs3

Вопрос:

Я разрабатываю одностраничное приложение, используя Vue.js 3 и машинописный текст.

Проблема затрагивает представление и отдельный файловый компонент. People.vue извлекает данные из серверной части и отображает их в нескольких PersonRow.vue компонентах с использованием v-for . Хотя типы свойств данных определены явно, я получаю предупреждение в консоли браузера:
[Vue warn]: Invalid prop: Type check failed for prop "person". Expected Person, got Object

Все работает нормально, и я мог бы изменить тип свойства на PersonRow.vue Object , чтобы избавиться от предупреждения, но я хотел бы, чтобы проверки типов работали правильно.

People.vue

 <template>
  <div class="container">
    <PersonRow v-for="person in people" :key="person.id" :person="person" />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { Person, getPeople } from '../services/person'
import PersonRow from '@/components/PersonRow.vue'

export default defineComponent({
  components: {
    PersonRow
  },
  data () {
    return {
      people: new Array<Person>()
    }
  },
  mounted () {
    getPeople().then(
      response => {
        this.people.push(...response)
      })
  }
})
</script>
  

PersonRow.vue

 <template>
  <div class="row">
    <div class="col">{{ person.givenName }}</div>
    <div class="col">{{ person.familyName }}</div>
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import { Person } from '../services/person'

export default defineComponent({
  props: {
    person: {
      type: Person,
      required: true
    }
  }
})
</script>
  

person.ts

 export class Person {
  constructor (id: number, givenName: string, familyName: string) {
    this.id = id
    this.givenName = givenName
    this.familyName = familyName
  }

  id: number;
  givenName: string;
  familyName: string;
}

export async function getPeople (): Promise<Person[]> {
  const response = await fetch('https://api.example.com/people')
  return await response.json() as Person[]
}
  

Ответ №1:

Аннотирование реквизита

 export default defineComponent({
  props: {
    person: {
      type: Object as PropType<Person>,
      required: true
    }
  }
})
  

Более глубокое погружение

Да, в документах говорится: кроме того, type также может быть пользовательская функция конструктора, и утверждение будет выполнено с instanceof проверкой

Но для того, чтобы заставить его работать, объект, переданный в prop, должен быть создан с new Person() помощью constructor . И проблема в том, что ваша getPeople () функция не создает массив Person экземпляров — это просто массив приведения типов обычных объектов JS, созданных с json() помощью to Person[] . Приведение к типу не изменяет тип объекта во время выполнения…

Комментарии:

1. Обратите внимание, что вам, возможно, придется import type { PropType } from 'vue' использовать PropType

Ответ №2:

«Person» не является типом. Типами могут быть массив, объект, строка, число, логическое значение, функция. Таким образом, проверки типов работают правильно.

Комментарии:

1. В документах указано, что возможны пользовательские типы: v3.vuejs.org/guide/component-props.html#type-checks

2. Но они означают классы JavaScript. Если ваш класс TypeScript переносится во время сборки, для Vue это просто объект во время выполнения (когда происходит проверка типа prop)

3. Также в документах говорится, что проверяется, что значение author реквизита было создано с new Person помощью, но ваша getPeople функция не вызывает Person конструктор, это просто приведение обычных объектов JS, десериализованных из JSON в Person[]

4. Спасибо, что упомянули об этом. PropType<T> позволило мне изменить мое определение типа с class на interface , что устраняет все накладные расходы на неиспользуемые конструкторы, пока вывод и проверка типов все еще работают.