What does the shims-tsx.d.ts file do in a Vue-Typescript project?

When creating a Vue project with typescript, two declaration files are included: shims-vue.d.ts and shims.tsx.d.ts.

//shims-vue.d.ts

declare module "*.vue" {
  import Vue from 'vue';
  export default Vue;
}

And:

//shims-tsx.d.ts

import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

While creating a small project (without the Vue CLI) I forgot to include the second (shims.tsx.d.ts) and my project compiles and runs as expected (without error).

I found this post about it: https://github.com/vuejs/vue-cli/issues/1198, but was hoping for more clarification.

I am just curious what this file does and why it is included? In other words, what I would have to do to "break" my app if I don't include this declaration file.

Thanks!


This link was the only decent explanation I could find. Basically it is to do with Webpack.

If you aren't using Webpack - you're only using Typescript, then doing something like this is an error:

import Foo from "./Foo.vue";

Because obviously Typescript doesn't understand .vue files - they aren't actually Typescript modules.

However, if you set up a Vue project you'll find that it does it all the time. How does that work? Webpack! As far as I can tell (I have tried to avoid insanity by learning anything at all about webpack), this is basically Webpack's job - it goes through all of the import files in your Javascript/Typescript, and "bundles" them, i.e. it merges them into one file.

But it extensible with "loaders" (terrible name) that can be added to handle particular file formats. For example you can configure it to use a CSS loader. That means when it finds

import "./foo.css"

It will bundle the CSS into the output, and probably add some Javascript to insert it into the DOM at runtime, or some nonsense like that.

Anyway, there's also (I presume) a loader for *.vue files that handles bundling those. So that's why import Foo from "./Foo.vue" works. Why do we need the shim file?

Because Typescript still isn't happy. It doesn't know anything about Webpack, so it will still throw an error when you try and import Foo.vue (it'll tell you Can't find module "./Foo.vue").

The solution is shims-vue.d.ts. The filename does not seem to be important, as long as it ends with .d.ts. I guess Typescript looks for all .d.ts in the same directory or something like that.

In any case, the contents are this:

declare module "*.vue" {
  import Vue from 'vue';
  export default Vue;
}

Which basically means, "every time you import a module with the name *.vue (wildcards are supported), then don't actually do it - instead treat it as if it had these contents".

This is how it seems to behave for me: If you do import Foo from "./Foo.vue" then the type of Foo will by Vue. There does not seem to be a way to actually import the Foo type.

Edit: Actually I think it works *if you import the component in another .vue file. If you import it from .ts then you just get an alias for Vue. This is annoying in tests! I made another question about this.


The first file helps your IDE to understand what a file ending in .vue is

The second file allows you to use .tsx files while enabling jsx syntaxsupport in your IDE to write JSX-style typescript code.