Búsqueda de sitios web

javascript - Usando el prop "as" de styled components con typescript


Actualmente estoy construyendo una biblioteca de patrones en la que he creado un componente Button usando React y styled-components. Basándome en el componente Button, quiero que todos mis componentes Link se vean exactamente igual y reciban exactamente las mismas props. Para ese propósito, estoy usando la prop as de styled-components, que me permite usar ese elemento ya construido como otra etiqueta o componente.

Componente de Botón

import * as React from 'react'
import { ButtonBorderAnimation } from './ButtonAnimation'
import { ButtonProps, ButtonVariant } from './Button.types'
import { ButtonBase, LeftIcon, RightIcon } from './Button.styled'

function Button({
  variant = ButtonVariant.Filled,
  children,
  leftIcon = null,
  rightIcon = null,
  ...props
}: ButtonProps): JSX.Element {
  return (
    <ButtonBase variant={variant} {...props}>
      {variant !== ButtonVariant.Ghost ? (
        <ButtonBorderAnimation {...props} />
      ) : null}
      {leftIcon ? <LeftIcon>{leftIcon}</LeftIcon> : null}
      {children}
      {rightIcon ? <RightIcon>{rightIcon}</RightIcon> : null}
    </ButtonBase>
  )
}

export default Button

Tipos de Botones

export interface ButtonProps {
  children: React.ReactNode
  variant?: 'filled' | 'outlined' | 'ghost'
  size?: 'small' | 'regular'
  underlinedOnHover?: boolean
  leftIcon?: React.ReactNode
  rightIcon?: React.ReactNode
  inverse?: boolean
}

export enum ButtonVariant {
  Filled = 'filled',
  Outlined = 'outlined',
  Ghost = 'ghost',
}

export enum ButtonSize {
  Small = 'small',
  Regular = 'regular',
}

Componente de enlace

import * as React from 'react'
import Button from '../Button/Button'
import { Link as LinkRouter } from 'react-router-dom'
import { LinkProps } from './Link.types'

function Link({ to, ...props }: LinkProps): JSX.Element {
  return <Button to={to} as={LinkRouter} {...props} />
}

export default Link

Tipos de enlace

import { ButtonProps } from '../Button/Button.types'
import { LinkProps } from 'react-router-dom'

type RouterLinkWithButtonProps = ButtonProps & LinkProps

export interface LinkProps extends RouterLinkWithButtonProps {}

Cuando hago lo anterior, este problema nos aparece...

Property 'to' does not exist on type 'IntrinsicAttributes & ButtonProps'.

...lo cual tiene sentido porque el botón no tiene la propiedad to que es requerida para el componente Link de react-router-dom.

¿Cómo abordarías algo como esto? Donde al usar el Button la propiedad to ni siquiera debería estar en el tipo y al usar el Link la propiedad to debería ser obligatoria.

Usos

<Button>Hello</Button>
<Link to="/somewhere">Hello</Link>

Respuesta #1

Esto debería funcionar.

function Link(_props: LinkProps): JSX.Element {
  const props = { as: LinkRouter, ..._props };
  return <Button {...props} />
}

Tenga en cuenta que TypeScript aplica verificaciones estrictas de asignación de literales de objetos, las cuales pueden relajarse asignando un literal de objeto a una variable previamente en lugar de pasarlo directamente, por ejemplo, como argumento de una función, lo cual es consistente con el comportamiento de asignación de props en componentes de React.

declare function foo(arg: { a: number }): void;

foo({ to: '', a: 1 }); // error

const arg = { to: '', a: 1 };
foo(arg); // no error

Artículos relacionados