为什么我的 Auth0 + Vue 3 设置会抛出 TypeScript 错误?
Why does my Auth0 + Vue 3 setup throw TypeScript errors?
我发现这个 this repo 为 Vue 2 创建了一个 Auth0-spa-js
TypeScript 插件,我在我的 Vue 3 项目中使用它。
但问题是,当使用像 auth.loginWithRedirect();
和 auth.logout();
这样的函数时,它会抛出一个 TypeScript 错误,提示 Object is of type 'unknown'. ts(2571)
.
我是 TypeScipt 的新手,不确定我做错了什么。
这是抛出错误的 link to my Vue 3 repo。并且抛出错误的代码的重要部分也复制在下面。
这是 Auth0 插件的样子:
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,
};
这就是我的 App.vue
文件的样子(错误显示在这个文件上):
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/profile">Profile</router-link> |
<router-link to="/faunaapi">API</router-link> |
<button @click.prevent="login">LOGIN</button> |
<button @click.prevent="logout">LOGOUT</button>
</div>
<router-view />
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
export default defineComponent({
name: "App",
setup() {
const auth = inject("Auth");
const login = (): void => {
auth.loginWithRedirect(); //<-- ERROR: Object is of type 'unknown'.
};
const logout = (): void => {
auth.logout({
returnTo: window.location.origin,
}); //<-- ERROR: Object is of type 'unknown'.
};
return {
logout,
login,
...auth,
};
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
你知道我做错了什么吗?
因此,如果您将鼠标悬停在“注入”上,您会看到如下内容:
(alias) inject<unknown>(key: string | InjectionKey<unknown>): unknown
特别是这里 inject<unknown>
,< >
里面是什么 Generic Type。
简单地说,您需要告诉 Inject 您要注入的是什么类型,所以它会推断它。
试试这个:
解决方案 1 - 传递类型参数(推荐):
import { Auth0Client } from "@auth0/auth0-spa-js";
import { defineComponent, inject } from "vue";
export default defineComponent({
name: "App",
setup() {
/* Here you pass Auth0Client types, the error should go away.
You should get type validation and autocompletion for `auth` now */
const auth = inject<Auth0Client>("Auth")!;
解决方案 2 - 铸造注入类型
// Here you are basically saying "Hey whatever inject returns, is an Auth0Client."
const auth = inject("Auth") as Auth0Client;
希望有用:)!
我发现这个 this repo 为 Vue 2 创建了一个 Auth0-spa-js
TypeScript 插件,我在我的 Vue 3 项目中使用它。
但问题是,当使用像 auth.loginWithRedirect();
和 auth.logout();
这样的函数时,它会抛出一个 TypeScript 错误,提示 Object is of type 'unknown'. ts(2571)
.
我是 TypeScipt 的新手,不确定我做错了什么。
这是抛出错误的 link to my Vue 3 repo。并且抛出错误的代码的重要部分也复制在下面。
这是 Auth0 插件的样子:
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,
};
这就是我的 App.vue
文件的样子(错误显示在这个文件上):
<template>
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/profile">Profile</router-link> |
<router-link to="/faunaapi">API</router-link> |
<button @click.prevent="login">LOGIN</button> |
<button @click.prevent="logout">LOGOUT</button>
</div>
<router-view />
</template>
<script lang="ts">
import { defineComponent, inject } from "vue";
export default defineComponent({
name: "App",
setup() {
const auth = inject("Auth");
const login = (): void => {
auth.loginWithRedirect(); //<-- ERROR: Object is of type 'unknown'.
};
const logout = (): void => {
auth.logout({
returnTo: window.location.origin,
}); //<-- ERROR: Object is of type 'unknown'.
};
return {
logout,
login,
...auth,
};
},
});
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
padding: 30px;
}
#nav a {
font-weight: bold;
color: #2c3e50;
}
#nav a.router-link-exact-active {
color: #42b983;
}
</style>
你知道我做错了什么吗?
因此,如果您将鼠标悬停在“注入”上,您会看到如下内容:
(alias) inject<unknown>(key: string | InjectionKey<unknown>): unknown
特别是这里 inject<unknown>
,< >
里面是什么 Generic Type。
简单地说,您需要告诉 Inject 您要注入的是什么类型,所以它会推断它。
试试这个:
解决方案 1 - 传递类型参数(推荐):
import { Auth0Client } from "@auth0/auth0-spa-js";
import { defineComponent, inject } from "vue";
export default defineComponent({
name: "App",
setup() {
/* Here you pass Auth0Client types, the error should go away.
You should get type validation and autocompletion for `auth` now */
const auth = inject<Auth0Client>("Auth")!;
解决方案 2 - 铸造注入类型
// Here you are basically saying "Hey whatever inject returns, is an Auth0Client."
const auth = inject("Auth") as Auth0Client;
希望有用:)!