When trying to pass props into setup using VueJS 3 Composition API and TypeScript, an error may occur stating: "Property 'user' does not exist on type"

I need help figuring out why TypeScript is not recognizing that props.user is of type UserInterface. Any advice or guidance would be greatly appreciated.

You can reach me at [email protected], [email protected], [email protected]. This seems to be more of a VueJS or TypeScript issue rather than a Quasar-related problem.

Here's the UserInterface for reference:

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

Component:

<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>

The VueJS 3 documentation https://v3.vuejs.org/guide/typescript-support.html#using-with-composition-api states:

In the setup() function, you don't need to specify types for the props parameter as they will be inferred from the component options.

However, I keep receiving a compilation error and I'm unsure about what I might be overlooking.

Result:

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 |     })

Notes: Adding a @ts-ignore above the line in question temporarily resolves the error, but it doesn't address the underlying issue.

I've attempted deleting node_modules and restarting everything to rule out any glitches.

This code is running within a Docker image.

Answer №1

When defining the validator and default in the prop declarations, the Vue documentation suggests either using an arrow function or providing an explicit this parameter:

CAUTION

Due to a design limitation in TypeScript regarding type inference of function expressions, special attention is needed when working 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>,
      // It is recommended to use arrow functions
      default: () => ({
        title: 'Arrow Function Expression'
      }),
      validator: (book: Book) => !!book.title
    },
    bookB: {
      type: Object as PropType<Book>,
      // Alternatively, provide an explicit this parameter
      default(this: void) {
        return {
          title: 'Function Expression'
        }
      },
      validator(this: void, book: Book) {
        return !!book.title
      }
    }
  }
})

Anders Hejlsberg, the lead architect of TypeScript, elaborates on this issue in a 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.


Original answer:

If one of your props doesn't align with the expected signature of PropOptions, it might disrupt the type inference for the props argument in setup(). This occurs when TypeScript fails to recognize that the signature of size.validator matches with the type of PropOptions.validator for some reason.

Interestingly, switching the validator to an arrow function resolves the type inference issue with props:

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

Answer №2

Using the withDefaults Syntax

I have found that the arrow function, as mentioned by @tony19, works perfectly with the withDefaults syntax.

For more information, you can refer to the documentation provided in the Vue Docs.

<script setup lang="ts">

interface Option {
    name: string;
    value: string;
}

interface Props {
    label?: string;
    options: Option[];
}
    
const props = withDefaults(defineProps<Props>(), {
    options: () => [
        { name: "Option 1", value: "1" },
        { name: "Option 2", value: "2" },
        { name: "Option 3", value: "3" },
    ],
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing event listeners with image elements in React for interactive typing experience

When I attempt to type the event as React.ChangeEvent<HTMLImageElement> in order to make the e.target.src work, I encounter the following error messages in the imageFound and ImageNotFound functions: Type '(evt: React.ChangeEvent) => void&a ...

Experience dynamic data transformations with Vue's server-side rendering feature

Incorporating Vue into server-side rendering presents a challenge when the content data within the template needs to be fetched from another CMS server. <template> <h1>{{ content.heading }}</h1> </template> <script> expo ...

What sets apart the utilization of add versus finalize in rxjs?

It appears that both of these code snippets achieve the same outcome: Add this.test$.pipe(take(1)).subscribe().add(() => console.log('added')); Finalize this.test$.pipe(take(1), finalize(() => console.log('finalized'))).sub ...

Converting an HTML page to PDF with Angular using jsPdf and Html2Canvas

[1st img of pdf generated with position -182, 0, image 208,298 ][1]Converting an HTML page to PDF in Angular 8+ using jspdf and Html2canvas has been a challenge. Only half of the page is successfully converted into PDF due to a scaling issue. Printing th ...

Nuxt Vuex global state update does not cause v-for loop components to re-render

I am struggling to effectively use Vuex global state with re-rendering child components in Vue.js. The global state is being mutated, but the data does not update in the v-for loop. Initially, all data is rendered correctly. However, when new data is intr ...

Unwrapping nested objects in a JSON array with JavaScript: A step-by-step guide

After trying some code to flatten a JSON, I found that it flattened the entire object. However, my specific requirement is to only flatten the position property. Here is the JSON array I am working with: [{ amount:"1 teine med 110 mtr iletau" comment:"" ...

Using Angular to parse intricate JSON data

Need help parsing an http request in the following format: [ { "id": 1, "date": "2022-01-13T00:00:00.000+00:00", "time": "2022-01-13T21:21:21.000+00:00", "office&quo ...

How can I force a Kendo UI Chart to resize in VueJS?

When integrating Kendo UI's Chart component within a Vue application, how can we trigger the chart to refresh or redraw? Although the chart automatically adjusts to changes in width by filling its parent container horizontally, noticeable stretching ...

javascript + react - managing state with a combination of different variable types

In my React application, I have this piece of code where the variable items is expected to be an array based on the interface. However, in the initial state, it is set as null because I need it to be initialized that way. I could have used ?Array in the i ...

Encountering the "Argument of type 'string' is not assignable to parameter of type 'never'" error when using Array.prototype.includes

The data type for keys is a combination of string[] | number[], which is derived from the ID type. The data type for id is simply ID. We want to check if the id exists within the array of keys. import React, { useState } from 'react'; type Distr ...

Prevent TypeScript file from being injected into index.html in Vue Cli 3

After setting up a project in typescript using the Vue Cli, I decided to separate the javascript code from the commands.ts file into a separate commands.html file. I placed the commands.html file in the public folder so that it gets copied to the dist fol ...

What is the best way to organize an array both alphabetically and by the length of its elements?

Imagine I am working with an array like this: ['a', 'c', 'bb', 'aaa', 'bbb', 'aa']. My goal is to sort it in the following order: aaa, aa, a, bbb, bb, c. this.array= this.array.sort((n1, n2) => ...

I am attempting to code a program but it keeps displaying errors

What is hierarchical inheritance in AngularJS? I have been attempting to implement it, but I keep encountering errors. import {SecondcomponentComponent} from './secondcomponent/secondcomponent.Component'; import {thirdcomponentcomponent} from & ...

Exploring Angular's APP_INITIALIZER: Comparing Promises and Observables

I have implemented an Angular v4 application where I need to retrieve settings from the server before the app starts. This is achieved using the APP_INITIALIZER: { provide: APP_INITIALIZER, useFactory: initializeSettings, deps: [SettingsService], ...

Vue.js Google Places Autocomplete Plugin

I'm currently working on integrating Google Places Autocomplete with Vue.js. According to the API documentation, the Autocomplete class requires an inputField:HTMLInputElement as its first parameter, like shown in their example: autocomplete = new g ...

The type 'Handles' does not contain the properties present in type 'TextInput'

Implementing a phone number field using React Native Paper and react-native-text-input-mask: // Necessary Imports import {TextInput} from 'react-native-paper' import TextInputMask from 'react-native-text-input-mask' // Control Implemen ...

Unable to grasp the mistake

My code contains a function: buy() { return new Promise((resolve, reject) => { this.http.request('http://192.168.1.131:8888/generatetoken.php') .subscribe(res => { resolve(res.text()); }); }).then((key) => ...

How can I customize the visibility toggles for the password input field in Angular Material?

Currently immersed in the Angular 15 migration process... Today, I encountered an issue with a password input that displays two eyes the first time something is entered in the field. The HTML code for this is as follows: <mat-form-field appearance=&qu ...

Tips for successfully typing the backtick character when transitioning to Typescript:

I am currently working on a Typescript Vue project involving Leaflet. I came across some code for lazy-loading map markers, but it was written in Javascript. Although the code works fine, I keep receiving errors and warnings from VSCode because this is not ...

What is the process of integrating BootstrapVue into our Vue.js project?

Currently, I am working with Vue.js and trying to integrate Bootstrap-vue into my project. However, I am encountering some issues where the screen turns black and none of the functionalities work properly once BootstrapVue is attached. Can someone please ...