javascript - 如何有条件地渲染按钮/使用 TypeScript 链接 Next.js 组件

标签 javascript reactjs typescript next.js next-link

我遇到了一个非常烦人的问题,就是在尝试有条件地渲染 Next.js Link 组件或按钮元素时,出现了一堆 typescript 错误。 因此,如果传递了 href 属性,我想渲染 Next.js 内置 Link 组件,否则它应该是 Button。 这只是错误的一个示例

Type 'HTMLAnchorElement' is missing the following properties from type 'HTMLButtonElement': disabled, form, formAction, formEnctype, and 11 more.ts(2322)

我不知道如何让 typescript 理解,如果存在 href 属性,请使用 (ButtonAsLinkProps & aProps),如果不存在,请使用 ButtonAsButtonProps。

我对 typescript 非常陌生,只尝试使用它几周,很确定,这里的类型是错误的。

这是我在这里发表的第一篇文章,我非常绝望,在互联网上的任何地方都找不到与此相关的任何主题。非常感谢任何帮助。该组件的代码如下:

interface BaseProps {
  children: ReactNode
  primary?: boolean
  className?: string
}

type ButtonAsButtonProps = BaseProps &
  Omit<DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, keyof BaseProps> & {
    href: undefined
  }

type ButtonAsLinkProps = BaseProps & LinkProps

type aProps = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof BaseProps>


type ButtonProps = (ButtonAsLinkProps & aProps) | ButtonAsButtonProps

const Button = ({
  children,
  className,
  href,
  primary,
  as,
  replace,
  scroll,
  shallow,
  passHref,
  prefetch,
  locale,
  legacyBehavior,
  ...rest
}: ButtonProps): JSX.Element | undefined => {
  const classNames = `${commonStyles} ${primary ? primaryStyles : secondaryStyles} ${className}`

  const linkProps = {
    as,
    replace,
    scroll,
    shallow,
    passHref,
    prefetch,
    locale,
    legacyBehavior,
  }
  if (href && linkProps) {
    return (
      <Link href={href} {...linkProps}>
        <a {...rest}>{children}</a>
      </Link>
    )
  }
  if (!href) {
    return (
      <button {...rest} className={classNames}>
        {children}
      </button>
    )
  }

}

export default Button

最佳答案

这是我不久前的解决方案,如果没有一些类型转换,我无法做到这一点,也许新版本的 TS 是可能的。

import React, { ForwardedRef, forwardRef } from 'react';
import Link, { LinkProps } from 'next/link';

type ButtonProps = JSX.IntrinsicElements['button'] & {
  href?: undefined;
};

type AnchorProps = JSX.IntrinsicElements['a'] & {
  href: string;
} & LinkProps;

type PolymorphicProps = ButtonProps | AnchorProps;
type PolymorphicButton = {
  (props: AnchorProps): JSX.Element;
  (props: ButtonProps): JSX.Element;
};

const isAnchor = (props: PolymorphicProps): props is AnchorProps => {
  return props.href != undefined;
};

export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, PolymorphicProps>(
  (props, ref) => {
    if (isAnchor(props)) {
      const { href, as, replace, scroll, shallow, passHref, prefetch, locale, ...rest } = props;
      const linkProps = { href, as, replace, scroll, shallow, passHref, prefetch, locale };

      return (
        <Link {...linkProps}>
          <a {...rest} ref={ref as ForwardedRef<HTMLAnchorElement>} />
        </Link>
      );
    }
    return (
      <button
        {...props}
        ref={ref as ForwardedRef<HTMLButtonElement>}
        type={props.type ?? 'button'}
      />
    );
  },
) as PolymorphicButton;
// Tests
// event object will have correct type in every case
<Button href="https://google.com" onClick={(e) => e} />;
<Button type="submit" onClick={(e) => e} />;
// @ts-expect-error target is provided, but href is not
<Button target="_blank" onClick={(e) => e} />;
<Button href="https://google.com" target="_blank" onClick={(e) => e} />;
<Button href="https://google.com" target="_blank" prefetch onClick={(e) => e} />;
// @ts-expect-error Next.js prefetch is provided, but href is not
<Button prefetch onClick={(e) => e} />;

或者,您可以从组件中删除有关 Next.js Link 的所有逻辑并像这样使用它:

<Link href="https://google.com" passHref={true}>
  <Button>link text</Button>
</Link>

关于javascript - 如何有条件地渲染按钮/使用 TypeScript 链接 Next.js 组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72866352/

相关文章:

javascript - 如何修复 gatsbyjs 中的 "Unknown argument ' slug' "?

javascript - react 警告 : Can't call setState (or forceUpdate) on an unmounted component

typescript - 如何让 TypeScript 加载 cordova 插件中包含的类型

angular - 具有不同属性名称的 typescript map 类型

javascript - 上传文件完成后如何禁用php中选择文件上传的按钮?

javascript - 使用一个按钮而不是两个按钮来更改字符串

javascript - AngularJS 警报上的 ng-show

javascript - 有没有办法阻止在最小宽度屏幕上加载内容

javascript - Storybook AddDecorators 不提供 redux 存储

javascript - 并行执行多个 promise ,但只从其中一个中检索结果