Hosting a Vue 3 application in .NET 6 Part 2: Enabling Typescript and Jest

vue_and_dotnet.png

Welcome back everyone. Hope you had time to read and absorb the first article in the series Hosting a Vue 3 application in .NET 6. We went over a lot with setting up our basic host in .NET but didn't really do much on the client application side. Today were not going to do any real development but were going to go ahead and configure typescript support since Vue 3 is written in typescript. The source code for this article series can be found on Github.

Packages

The first thing were going to need to do is add a few packages to our devDependencies in package.json. To enable support were going to need to add both ts-loader and typescript. Go ahead and add the latest of each so we can be using the latest and greatest, intellisense should show you the versions. Our setup is also going to require the installation of 2 types packages to the devDependencies so go ahead an add both @types/webpack-env and @types/jest Now that you have updated your package.json go ahead and on a terminal or command line run npm install to make sure your ready for development.

Adding a shim for Vue files

Since Typescript knows nothing about Vue, or heck even anything other than javascript or typescript, we need to add a shim so we can tell the compiler and webpack what to do with vue files when it sees an import statement with a .vue file. In the root of the ClientApp folder go ahead and add a new folder, name it @types. In this folder add a new file vue.d.ts. Your solution should look something like the following.

typesfolder_and_shim.PNG

Great, so now we need to add the actual shim. There are a few ways to do this but the strongest typed is that from the shim that you get from creating a vue cli project so were going to go ahead and use the same. Add the following code to the file

/* eslint-disable */
declare module '*.vue' {
    import type { DefineComponent } from 'vue'
    const component: DefineComponent<{}, {}, any>
    export default component
}

So what is this doing? Well its telling the typescript compiler to treat vue files as module and defining what a component is through the use of the DefineComponent function. Why do we need this? Well without it if you try to import a .vue file your going to get compile errors, the same is needed should you want to import something like a css file or mdx in react. Now I've been doing typescript for a while but not with Vue so this was new to me, after some Googling around the best explanation I could found was on a stack over flow post that linked to this blog about typescript modules. After reading this it will make more sense hopefully.

Typescript config

Now that we have Vue file support shimmed into our application we need to configure the typescript compiler. To do this go ahead an add a tsconfig.json to the root of the project. Add the following json to the file

{
  "compilerOptions": {
    "allowJs": true,
    "target": "esnext",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "moduleResolution": "node",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "useDefineForClassFields": true,
    "sourceMap": true,
    "baseUrl": ".",
    "paths": {
      "@t/*": [ "./ClientApp/@types/*" ],
      "@c/*": [ "./ClientApp/components/*" ],
      "@/*": [ "./ClientApp/*" ]
    },
    "types": [
      "webpack-env",
      "jest"
    ],
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },

  "exclude": [
    "./node_modules"
  ],

  "include": [
    "./ClientApp/@types/**/*.d.ts",
    "./ClientApp/**/*.ts",
    "./ClientApp/**/*.vue"
  ]
}

So what is this config doing? It may look like a lot but don't worry its not that scary. The config is broken into 3 main sections, the compilerOptions, the exclude and include sections. The last two as their name implies are paths to include and exclude so are fairly simple. We want to exclude node modules and we want all of our code to be included. Currently that would be ./ClientApp/@types typescript files and all typescript and vue files in any folder under ClientApp, well won't the second two catch the types? Yes but I like to be specific and not everyone puts the types in the client app folder, so its just a preference.

Compiler Options

So there are enough settings here that this warrants some discussion as there are quite a lot more settings that were not even using.

  • allowJs - as the name implies this allows us to also use plain javascript
  • target - what should the compiler output
  • module - same as target what kind of modules should be used
  • strict - tell the compiler to complain about pretty much everything
  • jsx - preserve any jsx
  • moduleResolution - use node modules when resolving
  • allowSyntheicDefaultImports - allow importing when there is no default export only other exports
  • forceConsistentCasingInFileNames - force you to use a consistent file naming pattern
  • useDefineForClassFields - Easier if you just read the documentation, but trust me you want it on
  • sourceMap - yes we want source maps so we can debug things
  • baseUrl - the base url for where we should start looking for ts files in the project
  • paths - include paths for the compiler, an alias for the folder resolve
  • types - typescript types to load and use, these are the npm packages we installed earlier
  • lib - typescript libraries we should load so the compiler knows about things

For a deeper explanation of these settings and others you can read through the docs.

Webpack

Previously we created a main.js and told webpack through our webpack.config.js to use this as the entry point to our application. To change over to typescript were going to need to first rename the file to main.ts , since the file is simple it can just be renamed and doesn't require any code changes. Second we need to update our webpack config. To do so we need to change the entry line from

entry: {
   main: './main.js',
},

to

entry: {
   main: './main.ts',
},

Great we have now updated our configuration and pointed webpack and the typescript compiler on where to start working from but were not done yet, we still need to add a loader. To do this we need to add a new rule to webpack.config.js. The rule should look like the following

{
   test: /\.ts$/,
   exclude: /node_modules/,
   use: [
      {
           loader: 'ts-loader',
           options: {
              configFile: path.resolve(__dirname, './tsconfig.json'),
              appendTsSuffixTo: [/\.vue$/]
           }
      }
    ]
},

What does this rule do? Well its telling webpack when it encounters a ts file that it needs to use the typescript loader we installed, it should resolve our config file and append ts to vue files for processing. Pretty simple right? Thats all we needed to turn on typescript support! You should now be able to run npm run dev/build on the command line and it should build just like before!

Wrapping Up

This was a quick article for our series but pretty important in my opinion. With Vue 2 it was hard to get typescript fully working right but with Vue 3 it was simple and quick which was one of the goals of the developers. Anyways I really hope you enjoyed and are getting thirsty to write some Vue 3 components in the Composition Api using Typescript so stay tuned for our next article where we will create a nav bar and components!

Disclaimer

All screenshots taken in Visual Studio community edition. Visual Studio is copyright Microsoft

0E-8 BEE
2 comments

Congratulations @farpetrad! You have completed the following achievement on the Hive blockchain and have been rewarded with new badge(s):

You distributed more than 1000 upvotes.
Your next target is to reach 1250 upvotes.

You can view your badges on your board and compare yourself to others in the Ranking
If you no longer want to receive notifications, reply to this comment with the word STOP

8E-8 BEE

Sorry I didn't get jest in the article like I meant to :facepalm: I will include it in the next one.

0E-8 BEE