DEV Community

Cover image for Creating Quasar Framework project with Typescript support (pre v1.9.6)
Xkonti
Xkonti

Posted on • Updated on • Originally published at xkonti.tech

Creating Quasar Framework project with Typescript support (pre v1.9.6)

Since release of Quasar 1.9.6 and Quasar app 1.6 this guide is inaccurate. Read the updated article if your using a recent version of Quasar.

Quasar Framework is an amazing Vue.js framework providing not only extensive set of well-designed UI components, but also basic project structure, multiple build targets and plenty of other developer-friendly features.

Today I want to show you how to setup a Quasar Framework project that has:

  • Support for Typescript
  • Support for class-based Vue components
  • Support for class-based Vuex modules

The code created by this tutorial (with minor improvements) is available as a project template on GitHub: xkonti/quasar-clean-typescript on a branch pre-v-1.9.6.

Quasar CLI

To start your journey with Quasar Framework, it is recommended that you globally install the Quasar CLI by running the command:

npm install -g @quasar/cli
Enter fullscreen mode Exit fullscreen mode

After it is installed, navigate to the directory in which you want to create the project. Now it's time to use the Quasar CLI to set things up for us:

quasar create my-new-project
Enter fullscreen mode Exit fullscreen mode

This will start the new project wizard where you have to provide some information:

  1. Project name - is simply your development project name. In this tutorial we'll leave it as it is suggested.

  2. Project product name - is the official name of the application you're building. In this tutorial we'll input: My New Project

  3. Project description - is simply a short description of your project. You can leave it empty. In this tutorial we'll input: A brand new project.

  4. Author - just input your name or nickname or leave it empty

  5. Pick your favourite CSS preprocessor - this setting won't affect any future steps of this tutorial so choose what you're familiar with. In this tutorial I'll choose the most recommended option: Sass with indented syntax

  6. Pick a Quasar components & directives import strategy - when using Vue components provided by the Quasar Framework, you'll need to import them first. Remember that you can always change this setting later in the quasar.config.js. There are a couple of options to choose from:

    • Auto-import in-use Quasar components & directives - this is a nice feature that lets the framework itself figure out what to import. Unfortunately, at the moment of writing this tutorial, this functionality doesn't work with Typescript, so we can't use it.
    • Manually specify what to import - this is the more tedious way of development, because it requires you to manually specify which components and directives you are using. You register them in the quasar.config.js file. We'll go with this option in this tutorial because it makes the application as light as possible.
    • Import everything from Quasar - this imports absolutely everything there is in Quasar. It's convenient for development but the resulting build also includes everything, so the app is huge.
  7. Check the features needed for your project - here we can select some additional features that are integrated into the Quasar Framework. In this tutorial, we'll select everything besides the IE11 support. Remember that you select/deselect options using spacebar and go to the next step using the enter key.

    • ESLint - a must have for a healthy code base
    • Vuex - a Vuex store that we will use as the introduction to this tutorial suggests
    • Axios - a promise based HTTP client
    • Vue-i18n - a translations solution for Vue projects
  8. Pick an ESLint preset - pick an ESLint preset of your choice. I recommend the Airbnb preset as it goes well with Typescript and enforces some good practices.

  9. Cordova/Capacitor id - if you plan building the project as a mobile app, input the Cordova or Capacitor ID. If not, leave it default (as in this tutorial).

  10. Should we run 'npm install' for you? - Choose whatever "Yes" option you prefer.

After this the whole project should be automatically generated and all dependencies downloaded. You can go to the newly created project directory and run the application in development mode with the command:

quasar dev
Enter fullscreen mode Exit fullscreen mode

Quick project overview

A Quasar project looks similar to most Vue.js projects. In the root folder there are package.json, README.md and plenty of config files. The quasar.conf.js is the main Quasar Framework config file where you specify used plugins, icon sets, components, directives, webpack options and various build options.

Next to those files is the src directory with quite a typical structure with the App.vue and index.template.html as a main component and base HTML template.

The boot directory contains some code related to plugins that runs before Vue instance start running. It's a perfect place to run some Vue.use(...).

The components directory is intended to contain your Vue components, but it's completely optional. The layouts directory is intended to contain application layout Vue components. Quasar has it's own QLayout component which allows you to quickly create familiar app layouts and has support for pages (the QPage component), which reside in the pages directory. This generated project is a simple example of the QLayout and the QPage components relation as well as their configuration in the Vue router, which is situated in the router directory.

There is also the store directory which contains a Vuex store with an example of a Vuex module.

Adding Typescript support

Now it's time to finally add Typescript support to the project. The Quasar team created a handy extension to the Quasar Framework that automatically upgrades your project to Typescript. To install it just run the command:

quasar ext add @quasar/typescript
Enter fullscreen mode Exit fullscreen mode

During the installation process the script will ask you a couple of questions:

  • Please choose how to derive webpack - choose the recommended option
  • Rename .js files to .ts - we are operating on a fresh Quasar project so it won't pose any problems - choose Yes
  • Will you use VSCode for this project? - it's up to you
  • Generate Prettier configuration - if you're planning on using Prettier, then definitely Yes
  • Overwrite ".eslintrc.js" - if you're asked this question, choose Owerwrite

If you'll try to run the project using the quasar dev command it will run the app but with two errors:

ERROR in C:/Dev/Tutorials/my-new-project/src/boot/i18n.ts(13,19):
TS7031: Binding element 'app' implicitly has an 'any' type.
ERROR in C:/Dev/Tutorials/my-new-project/src/store/index.ts(21,5):
TS2322: Type 'string | undefined' is not assignable to type 'boolean | undefined'.
  Type 'string' is not assignable to type 'boolean | undefined'.
Enter fullscreen mode Exit fullscreen mode

Typescript isn't happy about those two files. After changing their extensions to .ts they are checked more strictly. Let's quickly fix those errors.

Open the src/boot/i18n.ts file and add two lines above the default export:

// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
export default ({ app }) => {
  // Set i18n instance on app
  app.i18n = i18n;
};
Enter fullscreen mode Exit fullscreen mode

The @ts-ignore comment forces the typescript linter to ignore the fact, that the app argument is of type any (which is prohibited). The eslint-disable-next-line @typescript-eslint/ban-ts-ignore comment forces the typescript linter to ignore the rule to not allow usage of @ts-ignore. It's not the most elegant solution, but it's enough in this case since you probably won't be needing to change it anyway.

Now let's fix the problem with the store definition in the src/store/index.ts file. Open it and simply make sure that the strict property will get the boolean value by comparing it to the 'true' string:

export default function (/* { ssrContext } */) {
  const Store = new Vuex.Store({
    modules: {
      // example
    },

    // enable strict mode (adds overhead!)
    // for dev mode only
    strict: process.env.DEV === 'true',
  });

  return Store;
}
Enter fullscreen mode Exit fullscreen mode

Again, not very elegant, but it works. Now the project should run flawlessly.

Using class based Vue components

Ok. We have Typescript support, but all Vue components are still written the usual Vue way. To add support for class-based Vue components install 2 additional packages:

Install using npm:

npm install -S vue-class-component
npm install -S vue-property-decorator
Enter fullscreen mode Exit fullscreen mode

or using Yarn:

yarn add vue-class-component
yarn add vue-property-decorator
Enter fullscreen mode Exit fullscreen mode

Those two packages allow us to fully use our new powers of Typescript decorators. Let's convert all components that exist in the project.

App.vue

We need to change only the <script> contents:

import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default class App extends Vue {}
Enter fullscreen mode Exit fullscreen mode

Here we needed only to import the Component decorator and change the component into class.
The name of the component is now defined solely by the class name.

Error404.vue

This is basically the same process as with the App.vue:

import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default class Error404 extends Vue {}
Enter fullscreen mode Exit fullscreen mode

Index.vue

Same as App.vue and Error404.vue:

import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default class PageIndex extends Vue {}
Enter fullscreen mode Exit fullscreen mode

MyLayout view

Here we have an example of a component with simple data. In a class-based Vue component, data is represented just by class field:

import Vue from 'vue';
import Component from 'vue-class-component';

@Component
export default class MyLayout extends Vue {
  leftDrawerOpen = false;
}
Enter fullscreen mode Exit fullscreen mode

And that's all it took. If you want to learn more about class based Vue components, you can look into the two packages that we've just installed:

Using class based Vuex modules

The next step to enjoy the full glory of Typescript is to use class-based Vuex modules. It not only makes them more readable, but also way easier to use, as Typescript will know all the data types. Let's install the npm package that we need:

Install using npm:

npm install -D vuex-module-decorators
Enter fullscreen mode Exit fullscreen mode

or using Yarn:

yarn add -D vuex-module-decorators
Enter fullscreen mode Exit fullscreen mode

Now, delete the existing example Vuex module by removing whole src/store/module-example directory and fix the store declaration in src/store/index.ts:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  strict: process.env.DEV === 'true'
});
Enter fullscreen mode Exit fullscreen mode

Instead of exporting the function that creates the Vuex store and importing modules there, we are creating a simple store instance here. This will easily let us create modules in any place we want.

Let's create a Layout module as an example of a class-based Vuex module. It will store the layout's leftDrawerOpen state.

Create a file LayoutStoreModule.ts in the src/layouts folder:

import { Action, Module, Mutation, VuexModule } from 'vuex-module-decorators';
import Store from '../store/index';

@Module({
  dynamic: true,
  name: 'layout',
  namespaced: true,
  store: Store
})
export default class LayoutStoreModule extends VuexModule {
  public leftDrawerOpen = false;

  @Mutation
  public SET_LEFT_DRAWER_OPEN(value: boolean) {
    this.leftDrawerOpen = value;
  }

  @Action
  public setLeftDrawerOpen(value: boolean) {
    this.SET_LEFT_DRAWER_OPEN(value);
  }

  @Action
  public toggleLeftDrawer() {
    this.SET_LEFT_DRAWER_OPEN(!this.leftDrawerOpen);
  }
}
Enter fullscreen mode Exit fullscreen mode

First we're importing all needed classes from the vuex-module-decorators package and the store we created previously.

Next is the declaration of the module along with the @Module decorator that specifies some details:

  • dynamic: true - this make it a dynamic module, which means that you don't have register it in the store itself
  • name: 'layout' - this specifies a namespace of this module, so internally all module's mutations, actions etc. will be prefixed with it
  • namespaced: true - this marks a module as a namespaced module
  • store: Store - here we are linking the module to the store we created earlier

Then we create a module state leftDrawerOpen with a default value. It's simply a class field. Next we create a mutation that sets the leftDrawerOpen state. Below it there are two actions that allows us to set and toggle the state.

And that's it when it comes to store creation. Now let's use it in the MyLayout.vue component. First we are going to modify the script part of the file:

import Vue from 'vue';
import Component from 'vue-class-component';
import { getModule } from 'vuex-module-decorators';
import LayoutStoreModule from './LayoutStoreModule';

@Component
export default class MyLayout extends Vue {
  store = getModule(LayoutStoreModule);

  get leftDrawerOpen() {
    return this.store.leftDrawerOpen;
  }

  set leftDrawerOpen(value: boolean) {
    this.store.setLeftDrawerOpen(value);
  }
}
Enter fullscreen mode Exit fullscreen mode

We started by importing the getModule function that let's us get the dynamic module. Then we also imported the module class itself.

Inside the component we got rid of the leftDrawerOpen field and created the store field instead that will hold the Vuex module that we got from getModule.

Then we declared a computed property (get leftDrawerOpen()) that gets the store's state. We also created the setter (set leftDrawerOpen(value: boolean)) for this computed property so that it works well with the QDrawer component that might try to change the v-model if someone clicks outside of the opened drawer. Here we used the module's action to set the state.

In the template, we don't have to touch the v-model of the QDrawer component, as it's still the same name. We don't even have to change the @click event of the QBtn that opens/closes the drawer. But if we want to, we can use the toggleLeftDrawer action here in the template:

<q-btn
  flat
  dense
  round
  @click="store.toggleLeftDrawer()"
  icon="menu"
  aria-label="Menu"
/>
Enter fullscreen mode Exit fullscreen mode

If you want to learn more about class-based Vuex modules, take a look at the package we've just installed: vuex-module-decorators website

Conclusion

It's not only easy to configure the Quasar project to work well with TypeScript, but it's also fun to use class-based Vue components and class-based Vuex modules. I hope this will help some who are starting out with Quasar and Typescript.

The code created by this tutorial (with minor improvements) is available as a project template on GitHub: xkonti/quasar-clean-typescript on a branch pre-v-1.9.6.

GitHub logo Xkonti / quasar-clean-typescript

Simple Vue.js starter project using Quasar Framework, official typescript extension and couple additional packages for class based components and class based Vuex store modules.

Quasar - Clean Typescript Template

A simple Vue.js starter project using Quasar Framework with:

  • Axios
  • ESLint with Standard preset
  • Sass with SCSS syntax
  • TypeScript with class-based components
  • Vue-i18n
  • Vuex with class-based modules

Do it yourself

If you would like to recreate this project on your own or customize something, you can follow the tutorial explaining how this template was created.

Installation

  1. Clone this repository.
  2. Rename project name, description, author etc. in package.json and quasar.conf.js.
  3. Run yarn install or npm install command to install dependencies.
  4. Run dev script from package.json to run the dev server.

Previous versions

Top comments (15)

Collapse
 
gregveres profile image
gregveres

Nice article. And timely as I am just about to start a typescript based quasar project. Thank you for taking the time to put this together.

You have a typo that I thought I would point out.
"Now let's fix the problem with the store definition in the src/boot/i18n.ts file." is the first sentance in the section where you are about to describe the changes needed to the store file. Notice that the sentance is pointing to the i18n.ts file that you already fixed in the paragraph above it.

Greg

Collapse
 
gregveres profile image
gregveres

And actually, once I added in the === 'true', it still gives me an error in VSCode that process isn't defined. It suggests this:

Do you need to install type definitions for node? Try npm i @types/node and then add node to the types field in your tsconfig

Is this something that needs to be done? It doesn't quite seem right since we aren't talking about node.js at all.

Collapse
 
xkonti profile image
Xkonti • Edited

Thank you for pointing out this typo. I fixed it.

When it comes to the issue with missing definition of the process it requires the node.js typings. Keep in mind that this is mostly used during the build process and you don't have to worry about it in the production build. If you'll look into the repository of this template in the package.json in the devDependencies there is the @types/node included. I'm not sure why yours doesn't have it as I didn't add it manually.

Collapse
 
gregveres profile image
gregveres

for the missing definition of process, I think quiting VSCode and restarting it fixed the issue because it just went away without me doing anything to explicitly fix it.

do you know of a good article of how to structure a Vue/Vuex application for the real world? I am finding too many articles that try to teach Vuex without real world consideration for growth of the application. For instance, where is the split between Vue components and Vuex state when we get a non-trivial (ie, not another ToDo app) example.

Thanks for this article btw!

Thread Thread
 
xkonti profile image
Xkonti

It greatly depends on your applications. If you're building an application with lots of relatively not-complicated pages using a set of shared components the standard way of structuring the Vue app could be sufficient:

src/
├─ ...
├─ components
├─ layouts
├─ pages
└─ store
   ├─ some-module
   └─ other-module

But if you're building something more diverse you might want to go with more of a module-oriented structure: make separate modules where each module can contain structure described above. This is a good article on this topic: dev.to/maxpou/3-tips-for-scaling-l...

Keep in mind that project structure greatly depends on a personal preference and project type. Vue and Quasar Framework enable you to do whatever you want with it so you might need to take a trial and error approach to this problem to figure out what works for you best.

The point of separation of data between Vuex and a component is a whole another problem that requires some practice. I would suggest to minimize the amount of data in the Vuex store as lots of mutations can slow down the application. If you're planning building a larger application you might want to introduce distinctive separation between dumb components and smart components - never let your dumb components access the store directly. Let the smart components handle all the data and provide it to the dumb components.

You should read couple articles about those topics as lines are quite often blurry.

Collapse
 
devpixde profile image
Ingo Klemm

Yes, very helpful. I thought it would be easy to bring quasar and typescript together but now i spent the whole evening with a thing that should work out of the box (IMO).
Nevertheless your article helps a lot. Just one thing i stumbled over: in quasar.conf.js you set framework > all to false. So none quasar component will run after this is set. Maybe i missed it but in the following parts you never registered any quasar component. This is a bit irritating, at least for a beginner, since nothing works any more even the basic quasar starter template.

Collapse
 
gregveres profile image
gregveres

I recall reading somewhere that framework.all: auto doesn't work with typescript and that you should use framework.all: true to include everything (not a great idea), or framework.all: false and then manually add components as your site needs them.

I think that was somewhere on the quasar docs, but, like I said, I can't remember where I read it.

Collapse
 
devpixde profile image
Ingo Klemm

I have a further question: Does anybody know how to integrate electron with typescript support to a project? I have a running quasar typescript project but the electron code is still pure js. I tried to add things in tsconfig.json but the is not sufficient. TS under src-electron is just not compiled.

any hint or idea would be great, thanx.

Collapse
 
xkonti profile image
Xkonti

I'm very happy to hear that :) Good luck with your project!

Collapse
 
timsayshey profile image
Tim Badolato • Edited

VSCode is complaining about your code example. How do I resolve this?
test

Collapse
 
timsayshey profile image
Tim Badolato

I was able to fix the issue for myself by upgrading from Node 11 to Node 12 and running npm ci. No longer getting the validation error.

Collapse
 
mjforclaz profile image
Marcelo Javier

Hi, nice article, I put it in practice. Do you know a good way to implement Vuelidate library with Typescript & Quasar?

Collapse
 
xkonti profile image
Xkonti

Adding Vuelidate to this project seems to require some work. Of course you could just install it, add a boot file similar to the src/boot/axios.ts, and start using it, but there are options for typescript decorators etc. I have never used Vuelidate personally, so I can't be of much help here.

Collapse
 
ppulwey profile image
Patrick Pulwey

I wanted to say thank you. I always come back to this article when I have forgotten how to integrate TypeScript in a new project.
It would be nice if there was a ready-made template for it.

Collapse
 
xkonti profile image
Xkonti

I'm very happy to read that. And you may be happy that I wrote an updated version of this article about configuring a recent Quasar version.
Also, there's a ready-made template on GitHub available too 🎉