[英]Vue&TypeScript: how to avoid error TS2345 when import implemented in TypeScript component outside of project directory?
嘗試使用輔助組件(項目目錄外部)時出現TypeScript錯誤:
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>'.
我的WebStorm IDE沒有檢測到此錯誤; 當我使用TypeScript加載程序運行Webpack時,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;
}
從ui-kit-libary-in-development
名稱中可以ui-kit-libary-in-development
,這還不是npm-dependency,因此它現在不在node_modules
內部。
這完全是TypeScript錯誤。 盡管ts-loader
會拋出此錯誤,但Webpack會構建我的項目,並且已編譯的JavaScript可以正常工作。 如果執行以下操作之一,此錯誤將消失:
SimpleCheckbox.vue
與SkipProjectInitializationStepPanel.ts
移至同一目錄,並將其作為import SimpleCheckbox from './SimpleCheckbox.vue';
@Component({ template, components: { SimpleCheckbox } })
刪除SimpleCheckbox
並僅保留@Component({ template, components: {} })
(當然,在這種情況下,將不會呈現SimpleCheckbox
,但事實證明該問題是不在SkipProjectInitializationStepPanel
)。 ui-kit-libary-in-development
node_modules
主項目的node_modules
,並從ui-kit-libary-in-development
刪除node_modules
(如果不刪除,則不會有任何變化)。 不幸的是,我無法重現此問題。 由於某些原因,下面的重試效果沒有錯誤:
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>
我發現的所有線索都是該問題的注釋, 在組件裝飾器中設置組件會導致Typescript 2.4錯誤 :
側面組件應添加.d.ts才能正常運行。
從這個線索中,產生了以下問題:
.d.ts
? 最有可能在主項目中,但是如果是這樣,為什么我可以在vuetify
類的第三方庫中導入輔助組件? 因為那里有.d.ts
! .d.ts
聲明新的Vue組件? 一些教程或示例? 因為我無法重現此問題,並且我的項目仍然是原始項目(尚未獲得商業價值),所以我可以通過Google雲端硬盤( 下載zip存檔的鏈接)進行共享。 所有的node_modules
都包括在內,只需在main-project
目錄中運行npm run developmentBuild
。
如果您擔心潛在的病毒,也可以在此存儲庫中獲取源文件,但是由於它不包含node_modules
,因此,要進行復制,需要在main-project
和dependency
目錄中執行npm install
。
這已經是一個很長的答案。 如果您沒有時間閱讀所有內容,請在末尾找到TL; DR 。
最初,我不太了解TypeScript為什么提到VueClass<Vue>
並抱怨template
屬性。 但是,當我查看Component
裝飾器的類型定義時,情況變得更加清楚了:
vue-class-component/lib/index.d.ts
(部分省略)
declare function Component<V extends Vue>(options: ComponentOptions<V> & ThisType<V>): <VC extends VueClass<V>>(target: VC) => VC;
// ...
declare function Component<VC extends VueClass<Vue>>(target: VC): VC;
我們在這里看到的是Component
有兩個簽名。 第一個像您一樣使用,第二個不帶選項( @Component class Foo
)。
顯然,編譯器認為我們的用法與第一個簽名不匹配,因此它必須是第二個簽名。 因此,我們最終收到一條VueClass<Vue>
的錯誤消息。
注意:在最新版本(3.6.3)中,TypeScript實際上會顯示一個更好的錯誤,說明重載和不匹配的原因。
我要做的下一件事是暫時注釋掉main-project/node_modules/vue-class-component
的第二個函數聲明,並確保我們得到了不同的錯誤消息。 新消息跨越63行,因此我認為將其作為一個整體包含在這篇文章中是沒有意義的。
ERROR in /tmp/main-project/InitializeProjectGUI__assets/SingletonComponents/SkipProjectInitializationStepPanel/SkipProjectInitializationStepPanel.ts
../InitializeProjectGUI__assets/SingletonComponents/SkipProjectInitializationStepPanel/SkipProjectInitializationStepPanel.ts
[tsl] ERROR in /tmp/main-project/InitializeProjectGUI__assets/SingletonComponents/SkipProjectInitializationStepPanel/SkipProjectInitializationStepPanel.ts(10,24)
TS2322: Type '{ SimpleCheckbox: typeof SimpleCheckbox; }' is not assignable to type '{ [key: string]: VueConstructor<Vue> | FunctionalComponentOptions<any, PropsDefinition<any>> | ComponentOptions<never, any, any, any, any, Record<string, any>> | AsyncComponentPromise<any, any, any, any> | AsyncComponentFactory<...>; }'.
Property 'SimpleCheckbox' is incompatible with index signature.
Type 'typeof SimpleCheckbox' is not assignable to type 'VueConstructor<Vue> | FunctionalComponentOptions<any, PropsDefinition<any>> | ComponentOptions<never, any, any, any, any, Record<string, any>> | AsyncComponentPromise<any, any, any, any> | AsyncComponentFactory<...>'.
Type 'typeof SimpleCheckbox' is not assignable to type 'VueConstructor<Vue>'.
Types of property 'extend' are incompatible.
Type '{ <Data, Methods, Computed, PropNames extends string = never>(options?: import("/tmp/dependency/node_modules/vue/types/options").ThisTypedComponentOptionsWithArrayProps<import("/tmp/depende...' is not assignable to type '{ <Data, Methods, Computed, PropNames extends string = never>(options?: import("/tmp/main-project/node_modules/vue/types/options").ThisTypedComponentOptionsWithArrayProps<import("/tmp/main-...'.
...
Type 'import("/tmp/dependency/node_modules/vue/types/vnode").ScopedSlotReturnValue' is not assignable to type 'import("/tmp/main-project/node_modules/vue/types/vnode").ScopedSlotReturnValue'.
Type 'VNode' is not assignable to type 'ScopedSlotReturnValue'.
如您所見,該錯誤消息非常難以閱讀,並且並未真正指出特定的問題。 因此,我改為着手開始降低項目的復雜性,以便更好地理解問題。
我將為您省去涉及很多試驗和錯誤的整個過程。 讓我們直接跳轉到結果。
├─ main-project
│ ├─ node_modules // installed packages listed below (without sub-dependencies)
│ │ ts-loader@6.1.0
│ │ typescript@3.6.3
│ │ vue@2.6.10
│ │ vue-property-decorator@8.2.2
│ │ vuex@3.1.1
│ │ webpack@4.40.2
│ │ webpack-cli@3.3.9
│ ├─ SkipProjectInitializationStepPanel.ts
│ ├─ tsconfig.json
│ └─ webpack.config.js
└─ dependency
├─ node_modules // installed packages listed below (without sub-dependencies)
│ vue@2.6.10
└─ SimpleCheckbox.ts
main-project/tsconfig.json
{
"compilerOptions": {
"target": "es6",
"strict": true,
"moduleResolution": "node"
}
}
main-project/webpack.config.js
: module.exports = {
entry: './SkipProjectInitializationStepPanel.ts',
mode: 'development',
module: {
rules: [
{
test: /\.ts$/,
loader: 'ts-loader'
}
]
},
resolve: {
extensions: ['.ts', '.js']
}
};
main-project/SkipProjectInitializationStepPanel.ts
: import { Component } from 'vue-property-decorator';
import 'vuex';
import SimpleCheckbox from '../dependency/SimpleCheckbox';
Component({ template: '', components: { SimpleCheckbox } });
dependency/SimpleCheckbox.ts
: import Vue from 'vue';
export default class SimpleCheckbox extends Vue {}
當使用npm run webpack
從main-project
運行此示例時,我們得到與以前完全相同的錯誤。 任務完成。
當我刪除項目的各個部分以簡化為這個最小的示例時,我學到了一些非常有趣的東西。
您可能已經注意到我已添加到SkipProjectInitializationStepPanel.ts
的import 'vuex'
。 在原始項目中, vuex
當然是從不同位置導入的(例如main-project/Source/ProjectInitializer/Store/Store.ts
),但這對於重現該問題並不重要。 關鍵部分是main-project
導入vuex
而dependency
不導入 。
要找出為什么導入vuex
會導致此問題的原因,我們必須查看vuex
的類型定義。
vuex/types/index.d.ts
(前幾行)
import _Vue, { WatchOptions } from "vue";
// augment typings of Vue.js
import "./vue";
import { mapState, mapMutations, mapGetters, mapActions, createNamespacedHelpers } from "./helpers";
在這里,我們對第二次import
感興趣。 該評論實際上已經給了我們提示: 擴充Vue.js的類型 。
vuex/types/vue.d.ts
(省略部分)
import { Store } from "./index";
// ...
declare module "vue/types/vue" {
interface Vue {
$store: Store<any>;
}
}
這是罪魁禍首。 vuex
類型利用聲明合並將$store
添加到vue
的Vue
接口。 看來,這增強只發生在同一個“本地”類型node_modules
為vuex
。 因為main-project
具有增強的Vue
而dependency
具有原始Vue
,所以兩者不匹配。
在我所有的測試過程中, tsc
無法重現該問題。 顯然ts-loader
所做的事情有所不同,導致了此問題。 它可能與使用Webpack進行模塊解析有關,也可能完全不同。 我不知道。
我有一些想法可以解決或解決此問題。 盡管這些不一定是立即可用的解決方案,而是不同的方法和想法。
vuex
添加到dependency
因為從main-project
刪除vuex
並不是一個真正的選擇,所以我們要做的唯一一個使兩個Vue
接口都匹配的方法就是將vuex
也包含在dependency
中。
奇怪的是,我能夠在最小的示例中獲得此修復程序,但在原始項目中卻沒有。 我還不知道為什么。
除此之外,它還不是很優雅,您可能必須從main-project
引用的每個文件中導入vuex
。
node_modules
具有共享node_modules
文件夾的手段這兩個項目使用相同的vue
所以這個問題消失。 根據您的要求,將兩個項目共享一個相同的node_modules
文件夾來組織它們可能是一個很好的解決方案。 您可能還想看一下Lerna或Yarn工作區之類的工具,它們可以為您提供幫助。
dependency
作為包使用 您說dependency
不是npm-dependency 。 也許是時候做到這一點了。 與共享的node_modules
目錄類似,這將導致兩個項目使用相同的vue
安裝,這將解決此問題。
如前所述,這僅在ts-loader
發生。 也許可以在ts-loader
固定或配置某些東西來避免此問題。
main-project
導入vuex
而沒有dependency
。 vuex
使用聲明合並來增強Vue
接口, 並在其中添加$store
屬性。 現在Vue
從一個項目不匹配Vue
其他導致錯誤。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.