Vue 3+类型脚本:使用JS扩散操作符从setUp()返回的对象在vscode中抛出错误 [英] Vue 3 + TypeScript: Objects returned from setup() using the JS spread operator throw an error in vscode

查看:0
本文介绍了Vue 3+类型脚本:使用JS扩散操作符从setUp()返回的对象在vscode中抛出错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的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>

有人能搞清楚这里发生了什么吗?如何解决这个错误?

推荐答案

有几个问题:

  1. 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;
}
  1. Auth对象inject被定义为Auth0Client,而actual object provided in @/auth/index.ts的类型与Auth0Client不重叠。应导出实际类型,以便injectAuth对象可以键入引用的组件:
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);
  1. enable TypeScript support in a component(包括<template>内),组件定义应声明为defineComponent
import { defineComponent } from "vue";

export default defineComponent({
  ⋮
});
  1. 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

这篇关于Vue 3+类型脚本:使用JS扩散操作符从setUp()返回的对象在vscode中抛出错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆