menu

Questions & Answers

Problems with Vue 3.2 <script setup> tag and TypeScript types

I'm trying to use the Vue 3.2 <script setup> tag with TypeScript.

I have a simple use case where I want to display a user ID in the template.

My code is technically working. It displays the user ID just fine.

But there are two weird things...

  1. I have defined a user prop with a type of User that I imported from the Firebase SDK. This works, but I get an error on the User type that says: 'User' only refers to a type, but is being used as a value here. ts(2693). Why am I getting this error and how to fix it?

  2. The help docs say I do not need to import { defineProps } from "vue";. But if I don't do the import I get a 'defineProps' is not defined error. This is confusing. Why am I forced to import it when the docs say otherwise?

Here is my full code:

<template>
  <div>Logged in as {{ user.uid }}</div>
</template>

<script setup lang="ts">
import { defineProps } from "vue"; //Docs say this is not needed, but it throws an error without it
import { User } from "firebase/auth";

defineProps({
  user: {
    type: User, //ERROR: 'User' only refers to a type, but is being used as a value here. ts(2693)
    required: true,
  },
});
</script>
Comments:
2023-01-19 23:20:12
stackoverflow.com/a/64832307/909973 type: Object as PropType<User>, make sure to import PropType from vue
Answers(3) :

This solved the problem for me

.eslintrc.cjs

extends: [...],
env: {
  "vue/setup-compiler-macros": true,
},
rules: {...}

I don't recommend setting up global read only variables in the .eslintrc, that is not correct and only hides a portion of the configuration issue you may be seeing.

So to begin with, you'll want Volar installed in VSCode and uninstall vetur, and you'll need to add the vue eslint parser to your project with the following command:

npm install --save-dev eslint vue-eslint-parser

and in your .eslintrc.js file which should be in the root of your project (vue-project/.eslintrc) you'll want to set your parser to vue-eslint-parser. a simple .eslintrc file for a vue ^3.2.6 project can look like this:

module.exports = {
    root: true,
    env: {
        node: true,
    },
    extends: [
        'plugin:vue/vue3-essential',
        'plugin:vue/vue3-recommended',
    ],
    parser: "vue-eslint-parser",
}

this isn't a minimum example or even complete for vue but a decent start to fixing this specific option and getting linting in your project. note that adding more to the extends array may alter the linting and break this again, namely 'eslint:recommended'. And for what its worth, I'm not so sure Vue's out-of-the-box linting situation they provide is actually perfect and it still needs some work.

I'd recommend some additional reading at the following links:

https://vuejs.org/guide/scaling-up/tooling.html#linting

https://github.com/vuejs/vue-eslint-parser

https://github.com/vuejs/core/issues/4994#issuecomment-1125677494

Comments:
2023-01-19 23:20:12
You can’t say that the answer is incorrect, it was outdated, back then the eslint plugin for vue3 was not yet exist
2023-01-19 23:20:12
Hi @IhsanFajarRamadhan It seems that it is both wrong and outdated, I'm sorry to say. the global variables defined in that .eslintrc example don't cover the whole setup API, so it misses important build-time variables, and that answer also breaks linting in regular <script> tag, where the build-time variable will be actually undefined and never imported. So overall the answer solved the problem but missed nuance that could break builds, linting rules for commits, etc, etc.
2023-01-19 23:20:12
well I got that officialy from the old docs, you can see the history here github.com/vuejs/eslint-plugin-vue/commit/…
2023-01-19 23:20:12
I'm glad to see they've corrected their mistake on the matter and improved their linter configuration documentation!

in script setup you can define props like this instead:

<template>
  <div>Logged in as {{ user.uid }}</div>
</template>

<script setup lang="ts">
import { defineProps } from "vue"; //Docs say this is not needed, but it throws an error without it
import { User } from "firebase/auth";

defineProps<{ user: User }>();
</script>

another solution is as @bassxzero suggested, you can use

defineProps<{ user: { type: Object as PropType<User>; required: true } }>()

without using withDefaults it will be automaticly required

EDIT: The answer below this line is outdated, it was based on this old docs. you should check the newer docs here.

also, about:

import { defineProps } from "vue"; //Docs say this is not needed, but it throws an error without it

you actually don't need to import it, but you need to define it inside .eslitrc.js

globals: {
    defineProps: 'readonly',
    defineEmits: 'readonly',
    defineExpose: 'readonly',
    withDefaults: 'readonly'
  }
Comments:
2023-01-19 23:20:12
Actually I just realised that the .eslitrc.js globals didn't fully fix the problem. It stopped VScode from reporting it in the "problems" tab but TypeScript is still showing error 'defineProps' is not defined when I save the file. Do I also need to update tsconfig.json with something to make it work?
2023-01-19 23:20:12
what extension are u using? if you are using vetur, better disable it and change it to volar. Volar work best with script setup
2023-01-19 23:20:12
I am using Volar. But I just restarted VScode and the npm dev server, and the error went away! :)
2023-01-19 23:20:12
Using globals in the .eslintrc file doesn't really fix this, it just hides the configuration issue and isn't really correct. One should actually use Volar along with setting up their parser as vue-eslint-parser. If you what it done more correct please reference my answer.
2023-01-19 23:20:12
yes, this answer is outdated. the answer was based on the old docs github.com/vuejs/eslint-plugin-vue/commit/…