dev mode is fine.... everything works without error, however when I try to run quasar build on the same project:
I now see this error with both quasar dev and quasar build
1) Select the Composition API starter (with Typescript)
2) Have _traditional Options SFCs_ work as expected with full Typescript support. They appear to work, but when you use something on this.$q types are not resolved.
Note that to avoid confusion you can even select the starter without ESLint (as I have shown in my reproduction). This proves that it's a typescript setup and not some setup with my VSCode environment (which I know can be finicky).
Some details follow. The second post of this issue shows you specifically how to reproduce the issue I'm having.
Build mode........ spa
Pkg quasar........ v1.10.4
Pkg @quasar/app... v1.8.4
Debugging......... no
Publishing........ no
... I get a bunch of these types of errors...
TS2339: Property '$q' does not exist on type
I suspect this is something to do with Webpack handling typescript references. Is there something special I need to do for Typescript support during the build command? This is pretty easy to reproduce but let me know how I can help.
I decided not to be lazy and write down the minimal reproduction steps:
PS C:\Temp> quasar create test2
___
/ _ \ _ _ __ _ ___ __ _ _ __
| | | | | | |/ _` / __|/ _` | '__|
| |_| | |_| | (_| \__ \ (_| | |
\__\_\\__,_|\__,_|___/\__,_|_|
? Project name (internal usage for dev) test2
? Project product name (must start with letter if building mobile apps) Quasar App
? Project description A Quasar Framework app
? Author Robert <[email protected]>
? Pick your favorite CSS preprocessor: (can be changed later) SCSS
? Pick a Quasar components & directives import strategy: (can be changed later) Auto import
? Check the features needed for your project: TypeScript
? Pick a component style: Composition
? Cordova/Capacitor id (disregard if not building mobile apps) org.cordova.quasar.app
? Should we run `npm install` for you after the project has been created? (recommended) NPM
Quasar CLI 路 Generated "test2".
...
Note: I tried both Yarn and NPM for the last question.... same outcome (same error below).
> cd test2
> quasar dev
(works fine, no errors)
> quasar build
(works fine, no errors)
Now we make a simple edit to src/pages/Index.vue (added the test() method):
<template>
<q-page class="row items-center justify-evenly">
<example-component
title="Example component"
active
:todos="todos"
:meta="meta"
></example-component>
{{ test() }}
</q-page>
</template>
<script lang="ts">
import ExampleComponent from 'components/CompositionComponent.vue';
import { Todo, Meta } from 'components/models';
export default {
name: 'PageIndex',
components: { ExampleComponent },
data() {
const todos: Todo[] = [
{
id: 1,
content: 'ct1'
},
{
id: 2,
content: 'ct2'
},
{
id: 3,
content: 'ct3'
},
{
id: 4,
content: 'ct4'
},
{
id: 5,
content: 'ct5'
}
];
const meta: Meta = {
totalCount: 1200
};
return { todos, meta };
},
methods: {
test(){
return this.$q.screen;
}
}
}
</script>
Now quasar dev works fine--no errors at all... screen output displayed.
But with quasar build the new test() function is where we get the error:
ERROR in C:/Temp/test3/src/pages/Index.vue(50,19):
TS2339: Property '$q' does not exist on type '{ test(): any; }'.
app:build [FAIL] Build failed with errors. Check log above. +13ms
And the {{ test() }} in the template is just to show the output.
I did some more investigating and foremost let me acknowledge @IlCallo --thank you for your work on pushing Typescript into Quasar!!! I feel like https://github.com/IlCallo/quasar-typescript-starter should be mandatory reading for Typescript users. After digging into comments you've made, visiting Discord, etc. I believe I have a better understanding of what's going on....
If I'm correct when you selected the Typescript option you get the Typescript starter, where Index.vue is of the traditional "options" style SFC. The script tag reads: <script lang="ts"> but you won't get the expected Vue type augmentation ($q: QVueGlobals) unless you also modify:
export default {
...
}
to:
import Vue from "vue";
export default Vue.extend({
...
});
After this change quasar build works just fine. I will leave it to the experts; but I believe you can close this issue.
Another way of doing it is to let Quasar create a new TypeScript project for you and have a look at that
quasar create test-typescript
It will simply install the composition API for use in a Vue 2.x project..Then you'll see that they don't import Vue in every component but simply use a boot file to add the composition API as a Vue plugin
done by Quasar automatically when creating a new TypeScript project
npm install @vue/composition-api
quasar new boot composition-api.ts
// add boot: {"composition-api.ts"} to your quasar.conf.js file
/boot/composition-api.ts
import VueCompositionApi from '@vue/composition-api';
import { boot } from 'quasar/wrappers';
export default boot(({ Vue }) => {
Vue.use(VueCompositionApi);
});
When you have the boot file in place you can simply do this in your SFC:
components/CompositionComponent.vue
<template>
<div>
<p>{{ title }}</p>
<ul>
<li v-for="todo in todos" :key="todo.id" @click="increment">
{{ todo.id }} - {{ todo.content }}
</li>
</ul>
<p>Count: {{ todoCount }} / {{ meta.totalCount }}</p>
<p>Active: {{ active ? 'yes' : 'no' }}</p>
<p>Clicks on todos: {{ clickCount }}</p>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, ref } from '@vue/composition-api';
import { Todo, Meta } from './models';
function useClickCount() {
const clickCount = ref(0);
function increment() {
clickCount.value += 1;
return clickCount.value;
}
return { clickCount, increment };
}
function useDisplayTodo(todos: Todo[]) {
const todoCount = computed(() => todos.length);
return { todoCount };
}
export default defineComponent({
name: 'CompositionComponent',
props: {
title: {
type: String,
required: true
},
todos: {
type: (Array as unknown) as PropType<Todo[]>,
default: () => []
},
meta: {
type: (Object as unknown) as PropType<Meta>,
required: true
},
active: {
type: Boolean
}
},
setup({ todos }) {
return { ...useClickCount(), ...useDisplayTodo(todos) };
}
});
</script>
@DarkLite1 thanks for the comment but you haven't addressed the issue. I know exactly how to use the Composition API--I've been using it just fine. And as a habit I would suggest not including large code files in your issue comment unless you're trying to highlight changes that you've made.
The issue is this:
There are 3 ways to go with a SFC now: Options (traditional), Class-based (decorators), and Composition API. I will use the Composition API in many components, but I would like to retain the ability to write Options SFCs. The default Typescript starter comes with just such an example: Index.vue. It's a traditional SFC page--and it works beautifully when you quasar dev and quasar build. Where things break for Index.vue is when you start to try to access this, (e.g. this.$q). That's what my second post in this issue shows you how to do--reproduce that error.
This issue can be closed as far as I'm concerned. At the time of this issue there was a small omission in the Typescript starter, which I'm sure will be corrected by the time others read this. You must use Vue.component or Vue.extend with Typescript.
The other thing that I would personally recommend is going ahead and removing (or turning on) the @typescript-eslint/explicit-function-return-type rule from your .eslintrc.js. Typing your data() return types is not hard and will pay you dividends.
Lastly the linting defaults provided by Paolo is amazing--and will save you many hours of headaches. But if you're like me and you're using the AirBnB style you'll note that your IDE may drive you crazy with formatting. This is because Vetur uses a version of Prettier by default. Don't install the Prettier extension. Add this .prettierrc.js file so that your linting and formatting will line up better:
module.exports = {
singleQuote: true,
semi: true,
trailingComma: "es5",
arrowParens: "always",
}
You're right, we need to add the Vue.extends to all default components, I missed that one.
About this (and Airbnb formatting issues) feel free to open a PR on Quasar starter kit repo (note that my quasar-typescript-starter is a POC repo I did to study the TS integration, it's not actually used by Quasar in any way).
Care because you have to base your changes on dev branch and merge back to the same dev branch.
Because of technical limitations we cannot change the default branch, so you'll be proposed master branch instead: do not select it, use dev branch instead.
I'll leave the issue open to remind me about the problem. Unluckily I won't be able to work on this for some weeks 馃槄
am facing same issue, how to access this.$q under setup()
<script lang="ts">
// import { LoadingBar } from 'quasar'
import { defineComponent, onMounted } from '@vue/composition-api'
export default defineComponent({
name: 'AuthLayout',
setup () {
onMounted(() => {
this.$q.loadingBar.stop()
console.log('mounted!')
})
return { }
}
})
</script>
@diadal I think you need to have a look at the context object passed to the setup() method.
@diadal I found the $q object in context: like so:
export default defineComponent({
name: 'MyComponent',
setup(props, context) {
const router = context.root._router;
const q = context.root.$q;
}
});
Most helpful comment
@diadal I found the
$qobject in context: like so: