【发布时间】:2021-01-24 15:01:47
【问题描述】:
编辑:我在下面的帖子中多次说过我“不在模块中”。任何时候一个文件中的 JavaScript 代码从另一个技术上是一个模块的文件中调用。我的意思是这个模块是由 Quasar 在脚本初始化时自动调用的,预计会产生全局副作用。该模块永远不会导入到我编写的任何代码中。
简洁的帖子:
我正在使用带有 TypeScript 支持和 Vue 类组件的 Quasar 2.0.0。我希望在我的类中添加一个可访问的实例属性:this.$fetch(...);
我创建了一个包含以下内容的启动文件:
import Vue from "vue";
declare module "vue/types/vue"
{
interface Vue
{
prototype: {
$fetch: (resource: (string | Request), init?: RequestInit) => Promise<Response>
},
$fetch: (resource: (string | Request), init?: RequestInit) => Promise<Response>
}
}
type Context = {
Vue: Vue
};
export default async ({ Vue: vue }:Context) =>
{
vue.prototype.$fetch = async (resource, init) =>
{
return fetch(resource, init);
};
};
这不会引发任何 TypeScript 错误。但是当我尝试在组件中使用它时,我得到一个错误:
<template>
<q-layout>
<q-page-container>
<q-page class="flex flex-center">
<h1>Test</h1>
</q-page>
</q-page-container>
</q-layout>
</template>
<script lang="ts">
import { Vue, Component } from "vue-property-decorator";
@Component
export default class TestPage extends Vue
{
mounted ()
{
this.$fetch("https://www.google.com");
}
}
</script>
类型“TestPage”上不存在属性“$fetch”。
上一篇(杂乱无章)的帖子:
我正在开发 Quasar 应用程序,为了方便起见,我想创建一个修改后的 fetch 方法,如果请求的域与我的后端域匹配,则该方法会注入我的 API 密钥。界面类似于:
@Component
export default class VueComponent extends Vue
{
mounted()
{
this.$fetch("https://www.example.com/api/endpoint").then(console.log);
}
}
我知道如何定义这样的东西。我只需要根据https://vuejs.org/v2/cookbook/adding-instance-properties.html 更新Vue 原型:
Vue.prototype.$fetch = async function(request, init)
{
// ... blah ...
};
根据Quasar's documentation,更新 Vue 原型的适当时间在 引导文件 中。所以我创建了一个:
quasar new boot fetch
我的启动文件开始时相当简单:
export default async ({ Vue }) =>
{
Vue.prototype.$fetch = async (resource, init) =>
{
return fetch(resource, init);
};
};
当我尝试转换为 TypeScript 时,一切都崩溃了。参数Vue 隐式具有any 类型,因为没有为参数对象提供类型。
我尝试像这样定义类型:
import Vue from "vue";
type Context = {
Vue: Vue
};
export default async ({ Vue }:Context) =>
然而,这引起了关于变量阴影的抱怨(Vue 定义在上层范围内)。我注意到Vue 变量的类型是VueConstructor<Vue>,所以我改为尝试:
import { VueConstructor } from "vue";
type Context = {
Vue: VueConstructor
};
export default async ({ Vue }:Context) =>
这适用于函数定义,但在 Vue.prototype.$fetch = ... 行上引发错误:“不安全的成员访问 .$fetch 对任何值”。 TypeScript 不希望 Vue.prototype 具有 $fetch 属性。如果我要进行模块定义,我会像这样扩展接口:
declare module "abc" {
VueConstructor: {
prototype: {
$fetch: ...
}
}
}
但是我没有声明一个模块——这是一个引导文件。我尝试了一个快速破解来解决这个问题,只在我的引导文件的上下文中定义我自己的VueConstructor:
interface VueConstructor
{
prototype: {
$fetch: (resource: (string | Request), init?: RequestInit) => Promise<Response>
}
}
type Context = {
Vue: VueConstructor
};
export default async ({ Vue }:Context) =>
{
Vue.prototype.$fetch = async (resource: (string | Request), init?: RequestInit) =>
{
return fetch(resource, init);
};
};
这终于让 TypeScript 停止抱怨 this 文件,但是当我尝试在组件中使用 this.$fetch 时:
“VueComponent”类型上不存在属性“$fetch”。
由于VueComponent 扩展Vue 我想我需要更新Vue 类的定义,但我不知道如何做到这一点。我尝试将以下内容添加到我的启动文件中:
interface Vue
{
$fetch: (resource: (string | Request), init?: RequestInit) => Promise<Response>
}
但这没有用。我不能只使用declare module,因为我不在模块中。如何在没有模块的情况下全局扩展类型?
【问题讨论】:
-
阴影完全有效,但可以使用标准参数或内联重命名。请不要将您的个人风格规则与 TypeScript 错误混为一谈。
-
@AluanHaddad 这很公平,尽管即使解决了
Vue的问题(与VueConstructor相比)我不确定如何全局更新Vue接口,以便其他脚本会注意到我添加的新属性 -
将您的最后一个 sn-p 包装在
declare module "vue" {}中。通过在顶层添加export {}使其成为一个模块,如果你不在一个模块中 -
@AluanHaddad 刚刚尝试了一下,但没有解决。这个问题可能与 Quasar 对基于类的组件的使用有关。我正在创建的
VueComponent实际上并没有导入“vue”,而是导入了“vue-property-decorator”(import { Vue, Component } from "vue-property-decorator";)。我尝试使用declare module "vue-property-decorator"而不是declare module "vue",但这给出了“vue-property-decorator”不存在的错误?
标签: javascript typescript vue.js quasar-framework