Проблема с API композиции VueJS 3 и типом машинописи при передаче реквизитов в настройку: свойство «пользователь» не существует в типе

#typescript #vue.js #vuejs3 #quasar-framework #vue-composition-api

Вопрос:

Я изо всех сил пытаюсь понять , что я делаю неправильно, чтобы заставить TypeScript понять, что props.user это тип UserInterface , любые советы или указатели были бы удивительными

vue@3.1.1, typescript@4.2.2, quasar@2.0.0-rc.3. Это больше похоже на проблему с собственным VueJS или машинописным текстом, а не на что-либо связанное с Квазаром

Интерфейс пользователя для справки:

 export default interface UserInterface {
  id: number,
  email: string,
  name: string,
  agent_id: string
}
 

Компонент:

 <template>
  <q-avatar :color="color" :text-color="textColor" :size="size" :title="user.name" style="outline: 2px solid #ffffff">
    {{ initials(user.name) }}
  </q-avatar>
</template>

<script lang="ts">
import UserInterface from 'logic/interfaces/UserInterface'
import {computed, defineComponent, PropType} from 'vue'

const colors: Record<string, string> = {
  A: 'blue',
  K: 'black',
  R: 'purple',
  S: 'primary'
}

export default defineComponent({
  name: 'UserIcon',
  props: {
    user: {
      type: Object as PropType<UserInterface>,
      required: true
    },
    size: {
      type: String,
      required: false,
      default: 'lg',
      validator: function (value: string) {
        return ['xs', 'sm', 'md', 'lg', 'xl'].indexOf(value) !== -1
      }
    },
    textColor: {
      type: String,
      required: false,
      default: 'white'
    }
  },
  setup (props) {
    const initial = props.user.agent_id.charAt(0)
    const color = computed(() => {
      return colors[initial] || 'green'
    })

    return {
      color,
      initials (name: string) {
        const names = name.split(' ')
        let initials = names[0].charAt(0)
        if (names.length > 1) {
          initials  = names[names.length - 1].charAt(0)
        }

        return initials
      }
    }
  }
})
</script>
 

Документация VueJS 3 https://v3.vuejs.org/guide/typescript-support.html#using-with-composition-api государства:

В функции setup() вам не нужно передавать параметр ввода в props, так как он будет выводить типы из параметра компонента props.

Тем не менее, я продолжаю получать ошибку усложнения, и я не уверен, чего мне не хватает

Результат:

 Failed to compile.

TS2339: Property 'user' does not exist on type 'Readonly<LooseRequired<Readonly<{ [x: number]: string; } amp; { length?: number | undefined; toString?: string | undefined; toLocaleString?: string | undefined; concat?: string[] | undefined; join?: string | undefined; ... 15 more ...; includes?: ((searchElement: string, fromIndex?: number | undefined) => boolean) | un...'.
    38 |   },
    39 |   setup (props) {
  > 40 |     const initial = props.user.agent_id.charAt(0)
       |                           ^^^^
    41 |     const color = computed(() => {
    42 |       return colors[initial] || 'green'
    43 |     })
 

Примечания:
добавление точки @ts-ignore над соответствующей строкой устраняет ошибку, но это не решает проблему.

Я попытался удалить node_modules и перезапустить все, чтобы убедиться, что это не сбой

он работает в образе докера

Ответ №1:

Для validator и default в prop объявлениях документы Vue предписывают либо (1) использование функции со стрелкой, либо (2) предоставление явного this параметра:

предупреждение

Из-за конструктивных ограничений в машинописном тексте, когда дело доходит до вывода типов выражений функций, вы должны быть осторожны со validator default значениями и для объектов и массивов:

 import { defineComponent, PropType } from 'vue'

interface Book {
  title: string
  year?: number
}

const Component = defineComponent({
  props: {
    bookA: {
      type: Object as PropType<Book>,
      // Make sure to use arrow functions
      default: () => ({
        title: 'Arrow Function Expression'
      }),
      validator: (book: Book) => !!book.title
    },
    bookB: {
      type: Object as PropType<Book>,
      // Or provide an explicit this parameter
      default(this: void) {
        return {
          title: 'Function Expression'
        }
      },
      validator(this: void, book: Book) {
        return !!book.title
      }
    }
  }
})
 

Андерс Хейлсберг, ведущий архитектор TypeScript, объясняет проблему в комментарии на GitHub:

Это ограничение конструкции. Похоже на #38872. Функция со стрелкой[n] без параметров не зависит от контекста, но выражение функции без параметров зависит от контекста из-за неявного this параметра. Все, что зависит от контекста, исключается из первой фазы вывода типов, которая является фазой, определяющей типы, которые мы будем использовать для контекстно типизированных параметров. Итак, в исходном примере, когда значением a свойства является функция со стрелкой, нам удается сделать вывод для A , прежде чем мы назначим контекстный тип параметру b a . Но когда значение является выражением функции, мы не делаем никаких выводов, и a параметру присваивается тип unknown .


Оригинальный ответ:

Один из ваших реквизитов не соответствует ожидаемой сигнатуре PropOptions , что, по-видимому, нарушает вывод типа для props аргумента setup() . В частности, TypeScript не видит, что подпись size.validator PropOptions.validator по какой-то причине совпадает с типом.

Интересно, что если вы переключитесь validator на функцию со стрелкой, вывод типа для props завершится успешно:

 export default defineComponent({
  props: {
    size: {
      type: String,
      required: false,
      default: 'lg',
      //validator: function (value: string) { /*...*/ },
      validator: (value: string) => { /*...*/ },
    },
  }
})