带有Typescript注入的Vue 3无法按预期工作.排列类型只能从对象类型创建



Vue 3和TypeScript给出错误(见下文(,但仅当script lang=";ts";在场。有人能解释一下为什么Vue 3中带有Typescript标记的inject不起作用吗?

ERROR in src/components/global/HeaderMenu.vue:85:7
TS2698: Spread types may only be created from object types.
83 |     const auth = inject('Auth');
84 |     return {
> 85 |       ...auth,
|       ^^^^^^^
86 |     };
87 |   },
88 | });

工作示例:

<script>
import { defineComponent } from 'vue';
import { inject } from 'vue';
export default defineComponent({
name: 'HeaderMenu',
inject: ['Auth'],
methods: {
login() {
this.Auth.loginWithRedirect();
},
logout() {
this.Auth.logout();
this.$router.push({ path: '/' });
},
},
setup() {
const auth = inject('Auth');
return {
...auth,
};
},
});
</script>

生成上述错误的示例:

<script lang="ts">
import { defineComponent } from 'vue';
import { inject } from 'vue';
export default defineComponent({
name: 'HeaderMenu',
inject: ['Auth'],
methods: {
login() {
this.Auth.loginWithRedirect();
},
logout() {
this.Auth.logout();
this.$router.push({ path: '/' });
},
},
setup() {
const auth = inject('Auth');
return {
...auth,
};
},
});
</script>

Main.js

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
// Auth Service
import { Auth0 } from '@/auth';
import BootstrapVue3 from 'bootstrap-vue-3';
// Import Bootstrap an BootstrapVue CSS files (order is important)
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap-vue-3/dist/bootstrap-vue-3.css';

async function init() {
const AuthPlugin = await Auth0.init({
onRedirectCallback: (appState) => {
router.push(appState && appState.targetUrl ? appState.targetUrl : window.location.pathname);
},
clientId: 'xxxx',
domain: 'xxxx',
audience: process.env.VUE_APP_AUTH0_AUDIENCE,
redirectUri: window.location.origin,
});
const app = createApp(App);
app
.use(AuthPlugin)
.use(router)
.use(BootstrapVue3)
.mount('#app');
}
init();

auth.js示例

import createAuth0Client, {
Auth0Client,
GetIdTokenClaimsOptions,
GetTokenSilentlyOptions,
GetTokenWithPopupOptions,
LogoutOptions,
RedirectLoginOptions,
User
} from '@auth0/auth0-spa-js'
import {App, Plugin, computed, reactive, watchEffect} from 'vue'
import {NavigationGuardWithThis} from "vue-router";
let client: Auth0Client;
interface Auth0PluginState {
loading: boolean,
isAuthenticated: boolean;
user: User | undefined,
popupOpen: boolean;
error: any
}
const state = reactive<Auth0PluginState>({
loading: true,
isAuthenticated: false,
user: {},
popupOpen: false,
error: null,
})
async function handleRedirectCallback() {
state.loading = true;
try {
await client.handleRedirectCallback();
state.user = await client.getUser();
state.isAuthenticated = true;
} catch (e) {
state.error = e;
} finally {
state.loading = false;
}
}
function loginWithRedirect(o: RedirectLoginOptions) {
return client.loginWithRedirect(o);
}
function getIdTokenClaims(o: GetIdTokenClaimsOptions) {
return client.getIdTokenClaims(o);
}
function getTokenSilently(o: GetTokenSilentlyOptions) {
return client.getTokenSilently(o);
}
function getTokenWithPopup(o: GetTokenWithPopupOptions) {
return client.getTokenWithPopup(o);
}
function logout(o: LogoutOptions) {
return client.logout(o);
}
const authPlugin = {
isAuthenticated: computed(() => state.isAuthenticated),
loading: computed(() => state.loading),
user: computed(() => state.user),
getIdTokenClaims,
getTokenSilently,
getTokenWithPopup,
handleRedirectCallback,
loginWithRedirect,
logout,
}
const routeGuard: NavigationGuardWithThis<undefined> = (to: any, from: any, next: any) => {
const {isAuthenticated, loading, loginWithRedirect} = authPlugin;
const verify = async () => {
// If the user is authenticated, continue with the route
if (isAuthenticated.value) {
return next();
}
// Otherwise, log in
await loginWithRedirect({appState: {targetUrl: to.fullPath}});
}
// If loading has already finished, check our auth state using `fn()`
if (!loading.value) {
return verify();
}
// Watch for the loading property to change before we check isAuthenticated
watchEffect(() => {
if (!loading.value) {
return verify();
}
})
}
interface Auth0PluginOptions {
domain: string,
clientId: string,
audience: string,
redirectUri: string,
onRedirectCallback(appState: any): void
}
async function init(options: Auth0PluginOptions): Promise<Plugin> {
client = await createAuth0Client({
// domain: process.env.VUE_APP_AUTH0_DOMAIN,
// client_id: process.env.VUE_APP_AUTH0_CLIENT_KEY,
domain: options.domain,
client_id: options.clientId,
audience: options.audience,
redirect_uri: options.redirectUri,
});
try {
// If the user is returning to the app after authentication
if (
window.location.search.includes('code=') &&
window.location.search.includes('state=')
) {
// handle the redirect and retrieve tokens
const {appState} = await client.handleRedirectCallback();
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
options.onRedirectCallback(appState);
}
} catch (e) {
state.error = e;
} finally {
// Initialize our internal authentication state
state.isAuthenticated = await client.isAuthenticated();
state.user = await client.getUser();
state.loading = false;
}
return {
install: (app: App) => {
app.provide('Auth', authPlugin);
},
}
}
interface Auth0Plugin {
init(options: Auth0PluginOptions): Promise<Plugin>;
routeGuard: NavigationGuardWithThis<undefined>
}
export const Auth0: Auth0Plugin = {
init,
routeGuard
}

编译器无法从inject(...)调用中确定auth类型,因为它无法将其与相应的provide(...)调用相关联。它需要提供类型信息。

authPlugin类型需要暴露,例如:

export type TAuthPlugin = typeof authPlugin;

然后需要将其提供给inject。根据经验,像inject这样的函数是通用的:

const auth = inject<TAuthPlugin>('Auth');

首先,我认为您在使用inject(参见。https://v3.vuejs.org/guide/composition-api-provide-inject.html#using-注入(。我认为您可以删除注入属性。

然后还不清楚如何使用注入属性(通过Auth注入的对象或通过Auth对象的扩散属性(。

例如,我想通过将注入属性的类型设置为Auth0Plugin来修复typescript类型检查(但您需要首先导出接口(。

最新更新