I got below TypeScript error when tried to use side component (outside of project directory):
TS2345: Argument of type '{ template: string; components: { SimpleCheckbox: typeof SimpleCheckbox; }; }'
is not assignable to parameter of type 'VueClass<Vue>'.
Object literal may only specify known properties, and 'template' does not exist in type
'VueClass<Vue>'.
My WebStorm IDE did not detect this error; in was outputted in console when I ran Webpack with TypeScript loader.
The error occurs in:
import { Vue, Component, Prop } from 'vue-property-decorator';
import template from './SkipProjectInitializationStepPanel.pug';
import SimpleCheckbox from './../../../../ui-kit-libary-in-development/UiComponents/Checkboxes/MaterialDesign/SimpleCheckbox.vue';
@Component({ template, components: { SimpleCheckbox } }) // here !
export default class SkipProjectInitializationStepPanel extends Vue {
@Prop({ type: String, required: true }) private readonly text!: string;
}
As follows from ui-kit-libary-in-development
name, this is not npm-dependency yet, so it is not inside node_modules
for now.
It was exclusively TypeScript error; although ts-loader
casts this error, Webpack builds my project and compiled JavaScript works correctly. This error will disappear if to do one of below actions:
- Move
SimpleCheckbox.vue
to same directory asSkipProjectInitializationStepPanel.ts
and import it asimport SimpleCheckbox from './SimpleCheckbox.vue';
- Remove
SimpleCheckbox
from@Component({ template, components: { SimpleCheckbox } })
and leave only@Component({ template, components: {} })
(off course,SimpleCheckbox
will no be rendered in this case, but it proves that problem is not inSkipProjectInitializationStepPanel
). - Move
ui-kit-libary-in-development
tonode_modules
of main project and removenode_modules
fromui-kit-libary-in-development
(if don't remove, nothing will change).
Unfortunately, I could not reproduce this problem. For some reason below try of reproduction works without errors:
MainProject/src/Application.vue
<template lang="pug">
PageOne
</template>
<script lang="ts">
import { Vue, Component } from 'vue-property-decorator';
import PageOne from './PageComponents/PageOne'
@Component({ components: { PageOne }})
export default class Application extends Vue {
private created(): void {
console.log('Done.');
}
}
</script>
MainProject/src/PageComponents/PageOne.ts
import { Vue, Component, Prop } from 'vue-property-decorator';
import template from './PageOne.pug';
import Button from './../../../UiKitLibraryStillInDevelopment/UiComponents/Buttons/Button.vue';
@Component({ template, components: { Button } })
export default class SkipProjectInitializationStepPanel extends Vue {}
MainProject/src/PageComponents/PageOne.pug
.RootElement
Button(:text="'Click me'")
ui-kit-libary-in-development/UiComponents/Buttons/Button.vue
<template lang="pug">
button {{ text }}
</template>
<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator';
@Component
export default class SimpleCheckbox extends Vue {
@Prop({ type: String, required: true }) private readonly text!: string;
private created(): void {
console.log('OK!');
console.log(this.$props);
}
}
</script>
All clues what I found is this comment in issue Setting components in Component decorator causes Typescript 2.4 error:
Side components should add .d.ts for it to work AFAIK.
From this clue, the following question arising:
- Where I should to create
.d.ts
- in my main project or dependency? Most likely in main project, but if it so, why I can import side components in third-party libraries likevuetify
? Because there is.d.ts
there! - How I need to declare new Vue component in
.d.ts
? Some tutorial or example?
Source files for bounty
Because I could not reproduce this problem and my project still is raw (has not got commercial value yet), I can share it by Google Drive (link for downloading zip archive). All node_modules
are included, just run npm run developmentBuild
in main-project
directory.
If you are worry about potential viruses, you can also get source files in this repository, but because it is does not include node_modules
, for reproducing it's required to execute npm install
in both main-project
and dependency
directories.
As @lukasgeiter said,
Error will disappear, if delegate Type Check process to fork-ts-checker-webpack-plugin.
This has become quite a long answer. If you don't have time to read it all there's a TL;DR at the end.
Analysis
Error Message
At first I didn't really understand why TypeScript mentioning
VueClass<Vue>
and complaining about thetemplate
property. However, when I looked at the type definitions for theComponent
decorator, things became a bit clearer:vue-class-component/lib/index.d.ts
(parts omitted)What we can see here is that
Component
has two signatures. The first one to be used like you did, and the second one without options (@Component class Foo
).Apparently the compiler thinks our usage doesn't match the first signature so it must be the second one. Therefore we end up with an error message with
VueClass<Vue>
.Better Error Message
The next thing I did was temporarily comment out the second function declaration in
main-project/node_modules/vue-class-component
and sure enough we get a different error message. The new message spans 63 lines so I figured it wouldn't make sense to include it in this post as a whole.As you can see the error message is quite hard to read and doesn't really point to a specific problem. So instead I went ahead and started reducing the complexity of the project in order to understand the issue better.
Minimal Example
I'll spare you the whole process which involved lots of trial and error. Let's jump directly to the result.
Structure
main-project/tsconfig.json
main-project/webpack.config.js
:main-project/SkipProjectInitializationStepPanel.ts
:dependency/SimpleCheckbox.ts
:When running this example from
main-project
withnpm run webpack
, we get the exact same error as before. Mission accomplished.What's Happening?
While I was removing parts of the project to get it down to this minimal example, I learned something very interesting.
You might have noticed the
import 'vuex'
I've added toSkipProjectInitializationStepPanel.ts
. In the original projectvuex
is of course imported from different places (e.g.main-project/Source/ProjectInitializer/Store/Store.ts
) but that's not important for reproducing the issue. The crucial part is thatmain-project
importsvuex
anddependency
doesn't.To find out why importing
vuex
causes this issue, we have to look at the type definitions ofvuex
.vuex/types/index.d.ts
(first few lines)Here we are interested in the second
import
. The comment actually gives us a hint already: augment typings of Vue.js.vuex/types/vue.d.ts
(parts omitted)And here we have the culprit. The
vuex
types make use of declaration merging to add$store
to theVue
interface ofvue
. It seems that this augmentation only happens to "local" types in the samenode_modules
asvuex
. Becausemain-project
has the augmentedVue
anddependency
has the original one, the two don't match.TS Loader
During all my testing I couldn't reproduce the problem with just
tsc
. Apparentlyts-loader
does something differently causing this issue. It could have to do with it using Webpack for module resolution or it could be something entirely different. I don't know.Solution Approaches
I have some ideas how this problem could be solved or worked around. Although these are not necessarily ready-to-use solutions but rather different approaches and ideas.
Add
vuex
todependency
As removing
vuex
frommain-project
isn't really an option, the only thing we can do to make bothVue
interfaces match, is includevuex
independency
as well.The odd thing here is that I was able to get this fix working in my minimal example but not in the original project. I haven't figured out why that is.
In addition to that, it's not very elegant and you might have to import
vuex
from every file that you reference frommain-project
.Use a shared
node_modules
Having a shared
node_modules
folder means both projects use the samevue
so this problem goes away. Depending on your requirements it might be a good solution to organize the two projects in a way that they share the samenode_modules
folder. You might also want to take a look at tools like Lerna or Yarn workspaces which can help with this.Consume
dependency
as a packageYou say that
dependency
is not [an] npm-dependency yet. Maybe it's time to make it one. Similarly to a sharednode_modules
directory this would result in both projects using the samevue
installation which should fix the issue.Investigate further
As mentioned before, this only happens with
ts-loader
. Maybe there is something that can be fixed or configured ints-loader
to avoid this problem.TL;DR
main-project
importsvuex
whiledependency
doesn't.vuex
augments theVue
interface using declaration merging adding a$store
property to it. Now theVue
from one project doesn't matchVue
from other causing an error.