This link was the only decent explanation I could find. Basically it has 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 is 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.