将道具传递到设置时,VueJS 3 Composition API 和 TypeScript 类型出现问题:类型上不存在 属性 'user'

VueJS 3 Composition API and TypeScript type issue when passing props into setup: Property 'user' does not exist on type

我正在努力找出我做错了什么让 TypeScript 理解 props.userUserInterface 类型,任何建议或指示都会很棒

vue@3.1.1,typescript@4.2.2,quasar@2.0.0-rc.3。这感觉更像是原生 VueJS 或 TypeScript 问题,而不是与 Quasar

有任何关系

供参考的用户界面:

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 状态:

On setup() function, you don't need to pass a typing to props parameter as it will infer types from props component option.

但是,我一直收到复杂的错误,我不确定我错过了什么

结果:

Failed to compile.

TS2339: Property 'user' does not exist on type 'Readonly<LooseRequired<Readonly<{ [x: number]: string; } & { 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 并重新启动所有内容以确保它不是故障

其 运行 在 docker 图像中

对于 prop 声明中的 validatordefaultVue docs 规定 (1) 使用箭头函数,或 (2) 提供显式 this参数:

WARNING

Because of a design limitation in TypeScript when it comes to type inference of function expressions, you have to be careful with validator and default values for objects and arrays:

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 的首席架构师 Anders Hejlsberg 在 GitHub comment:

中解释了这个问题

This is a design limitation. Similar to #38872. A[n] arrow function with no parameters is not context sensitive, but a function expression with no parameters is context sensitive because of the implicit this parameter. Anything that is context sensitive is excluded from the first phase of type inference, which is the phase that determines the types we'll use for contextually typed parameters. So, in the original example, when the value for the a property is an arrow function, we succeed in making an inference for A before we assign a contextual type to the a parameter of b. But when the value is a function expression, we make no inferences and the a parameter is given type unknown.


原回答:

你的一个道具与 PropOptions 的预期签名不匹配,这显然打破了 setup()props 参数的类型推断。具体来说,由于某种原因,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) => { /*...*/ },
    },
  }
})