我有一个简单的Vue 3 + TypeScript repo,我正在尝试集成Auth0插件。
它在前台显示经过字符串处理的user
对象,工作正常。
但Visual Studio代码显示键入错误Cannot find name 'user'. ts(2304)
,因为它在...auth
扩散运算符内返回时无法看到对象user
。
我不确定它为什么这样做,也不知道如何解决它。
这是Auth0插件的代码。简而言之,它使用app.provide("Auth", authPlugin);
提供对一系列内容访问,包括user
对象:
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,
};
在我的Profile.vue
页面中,我使用const auth = inject<Auth0Client>("Auth")!;
注入Auth0插件,并使用...auth
散布操作符从setup()
返回其所有内容。这包括现在可以在模板中使用的user
对象。
所有这些都在前端发挥作用。它会按预期显示字符串化为user
的对象。
但vscode引发了Cannot find name 'user'. ts(2304)
错误,因为user
对象未从setup()
显式返回。
它似乎不知道...auth
扩散运算符在auth
内部有user
对象:
<template>
<div class="about">
<h1>This is a profile page, only logged in users can see it.</h1>
</div>
<div class="row">
{{ JSON.stringify(user, null, 2) }} <!-- ERROR: Cannot find name 'user'.ts(2304) -->
</div>
</template>
<script lang="ts">
import { Auth0Client } from "@auth0/auth0-spa-js";
import { inject } from "vue";
export default {
name: "Profile",
setup() {
const auth = inject<Auth0Client>("Auth")!;
return {
...auth,
};
},
};
</script>
我曾尝试通过显式返回user
对象来解决此问题,如下所示,但它破坏了功能。字符串user
对象不再显示在前端:
<template>
<div class="about">
<h1>This is a profile page, only logged in users can see it.</h1>
</div>
<div class="row">
{{ JSON.stringify(auth_user, null, 2) }}
</div>
</template>
<script lang="ts">
import { Auth0Client } from "@auth0/auth0-spa-js";
import { inject } from "vue";
export default {
name: "Profile",
setup() {
const auth = inject<Auth0Client>("Auth")!;
const auth_user = auth.getUser(); // This does not work
//const auth_user = auth.user; // This variation also doesn't work
return {
auth_user,
};
},
};
</script>
有人能搞清楚这里发生了什么吗?如何解决这个错误?
有几个问题:
Auth0Client
class没有user
字段,因此从setup()
返回{ ...auth }
不会创建user
属性。但这不是您想要的类型,正如我们在下一点中看到的那样。
export default class Auth0Client {
private options;
private transactionManager;
private cacheManager;
private customOptions;
private domainUrl;
private tokenIssuer;
private defaultScope;
private scope;
private cookieStorage;
private sessionCheckExpiryDays;
private orgHintCookieName;
private isAuthenticatedCookieName;
private nowProvider;
cacheLocation: CacheLocation;
private worker;
constructor(options: Auth0ClientOptions);
private _url;
private _getParams;
private _authorizeUrl;
private _verifyIdToken;
private _parseNumber;
private _processOrgIdHint;
buildAuthorizeUrl(options?: RedirectLoginOptions): Promise<string>;
loginWithPopup(options?: PopupLoginOptions, config?: PopupConfigOptions): Promise<void>;
getUser<TUser extends User>(options?: GetUserOptions): Promise<TUser | undefined>;
getIdTokenClaims(options?: GetIdTokenClaimsOptions): Promise<IdToken>;
loginWithRedirect(options?: RedirectLoginOptions): Promise<void>;
handleRedirectCallback(url?: string): Promise<RedirectLoginResult>;
checkSession(options?: GetTokenSilentlyOptions): Promise<void>;
getTokenSilently(options: GetTokenSilentlyOptions & {
detailedResponse: true;
}): Promise<GetTokenSilentlyVerboseResponse>;
getTokenSilently(options?: GetTokenSilentlyOptions): Promise<string>;
private _getTokenSilently;
getTokenWithPopup(options?: GetTokenWithPopupOptions, config?: PopupConfigOptions): Promise<string>;
isAuthenticated(): Promise<boolean>;
buildLogoutUrl(options?: LogoutUrlOptions): string;
logout(options?: LogoutOptions): Promise<void> | void;
private _getTokenFromIFrame;
private _getTokenUsingRefreshToken;
private _getEntryFromCache;
}
Auth
对象inject
被定义为Auth0Client
,而actual object provide
d in @/auth/index.ts
的类型与Auth0Client
不重叠。应导出实际类型,以便inject
Auth
对象可以键入引用的组件:
const authPlugin = {
isAuthenticated: computed(() => state.isAuthenticated),
loading: computed(() => state.loading),
user: computed(() => state.user),
getIdTokenClaims,
getTokenSilently,
getTokenWithPopup,
handleRedirectCallback,
loginWithRedirect,
logout,
};
export type ProvidedAuthPlugin = typeof authPlugin;
⋮
app.provide("Auth", authPlugin);
到enable TypeScript support in a component(包括<template>
内),组件定义应声明为defineComponent
:
import { defineComponent } from "vue";
export default defineComponent({
⋮
});
和Auth
对象的类型应在inject
组件中使用:
import type { ProvidedAuthPlugin } from "@/auth";
import { inject, defineComponent } from "vue";
export default defineComponent({
name: "Profile",
setup() {
const auth = inject("Auth") as ProvidedAuthPlugin;
return {
...auth,
};
},
});
GitHub PR