Vue3: cannot access computed prop from another computed prop when using TypeScript

Viewed 2480

I have a Vue3 project with TypeScript, and I'm finding I cannot access the properties (using dot notation or named indexing) of the returned JS object from one computed property in another computed property.

So given the code below, my TS compiler will have a compilation error trying to read friends on the this.user object. This makes sense as this.user is a function, but in the Vue world it's treated as a property. And the code will work fine if the lang="ts" part is removed.

<script lang="ts">
import { defineComponent } from "vue";

export default defineComponent({
  name: "HelloWorld",
  props: {
    msg: String,
  },
  computed: {
    user: function(): { friends: [] } {
      return { friends: [] };
    },
    formattedFriends: function() {
      return this.user.friends.map((f: any) => f);
    },
  },
});
</script>

Here is the error:

Failed to compile.

src/components/HelloWorld.vue:92:24
TS2339: Property 'friends' does not exist on type '() => { friends: never[]; }'.
    90 |     },
    91 |     formattedFriends: function() {
  > 92 |       return this.user.friends.map((f: any) => f);
       |                        ^^^^^^^
    93 |     },
    94 |   },
    95 | });

I created this sample code with the Vue cli (vue create).

I'm not sure if this is even a problem with TypeScript or Vue? Any ideas? I don't want to remove the TypeScript tag for this code, but might be the best option.

3 Answers

Might not be the best solution but I guess you can appease the compiler by specifying the this parameter ..

formattedFriends: function(this: any) { // or a stricter type if you wish
  return this.user.friends.map((f: any) => f);
},

One way would be to convert the type before accessing it:

computed: {
    user: function(): { friends: [] } {
        return { friends: [] };
    },
    formattedFriends: function() {
        const typedUser = (this.user as { friends: [] })
        
        return typedUser.friends.map((f: any) => f);
    },
},

Interested to know if there's a better way though.

not sure what your user computed is supposed to do as it has no reactive source?

however, in Vue3, its much nicer to use the reactivity API, and do something like

export default defineComponent({
  name: "HelloWorld",
  props: {
    msg: String,
  },
  setup() {
    const user = reactive({friends:[]})
    const formattedFriends = computed(() => user.friends.map((f : any) => f))
    return {
      user,
      formattedFriends
    }
  },
});
Related