Vue 3 y TypeScript dan un error (ver más abajo) PERO solo cuando está presente el script lang="ts". ¿Alguien puede ayudar a explicar por qué inject en Vue 3 con la etiqueta Typescript no funciona?
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 | });Ejemplo de trabajo:
<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>Ejemplo que genera el error anterior:
<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>Principal.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();Ejemplo de 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 }Primero, creo que está haciendo algo mal con la forma en que usa inyectar (cf. https://v3.vuejs.org/guide/composition-api-provide-inject.html#using-inject ). Creo que puedes eliminar la propiedad de inyección.
Entonces no está claro cómo usa las propiedades de inyección (a través del objeto inyectado Auth o a través de las propiedades de propagación del objeto Auth).
Supongo que para corregir la verificación del tipo de mecanografiado configurando el tipo de propiedad inyectada en Auth0Plugin por ejemplo (pero primero deberá exportar la interfaz).
No hay forma de que un compilador determine el tipo de auth de la llamada inject(...) porque no puede asociarlo con la llamada provide(...) . Debe proporcionarse información de tipo.
El tipo authPlugin debe estar expuesto, por ejemplo:
export type TAuthPlugin = typeof authPlugin; Entonces debe proporcionarse para inject . Como regla general, las funciones como inject son genéricas:
const auth = inject<TAuthPlugin>('Auth');