JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 14309
  • Score
    100M100P100Q127404F
  • License MIT

A wrapper around React.forwardRef() that allows HTML attributes and prop types to be derived from the as prop.

Package Exports

  • forward-ref-as
  • forward-ref-as/package.json

Readme


forwardRefAs()

A wrapper around React.forwardRef() that allows HTML attributes and prop types to be inferred from an as prop.
npm i forward-ref-as

Bundlephobia Types Code coverage Build status NPM Version MIT License


Quick start

import forwardRefAs from 'forward-ref-as'
import type {AsProp} from 'forward-ref-as'

// Forwards `ref` to the underlying button and adds strong
// types for the `as` prop.
const Button = forwardRefAs<ButtonProps, 'button'>(
  ({as: As = 'button', ...props}, ref) => <As ref={ref} {...props} />
)

interface ButtonProps {
  as?: AsProp
}

// ✅ Will pass type checking and autocomplete correctly
<Button as='a' href='https://jaredLunde.com'/>

// ❌ Will fail type checking and not autocomplete "href"
<Button href='#'>

API

forwardRefAs()

A wrapper around React.forwardRef() with the same call signature, but a type signature that allows as prop HTML attributes and React prop types to be inferred.

function forwardRefAs<Props, DefaultAs extends AsProp = 'div'>(
  render: React.RefForwardingComponent<
    DefaultAs extends keyof JSX.IntrinsicElements
      ? FromReactType<DefaultAs>
      : DefaultAs,
    Props
  >
): ForwardRefAsExoticComponent<Props, DefaultAs>

Types

AsProp

/**
 * These are the types accepted by an "as" prop
 */
export type AsProp = React.ReactType | keyof JSX.IntrinsicElements

ForwardRefAsExoticComponent

/**
 * This is a signature that matches `ForwardRefExoticComponent`, but allows for
 * inheriting attributes via the "as" prop and gets rid of `propTypes` because,
 * dang it, this is TypeScript! Get that outta here.
 */
export type ForwardRefAsExoticComponent<Props, DefaultAs extends AsProp> = Pick<
  React.ForwardRefExoticComponent<DefaultAs>,
  Exclude<keyof React.ForwardRefExoticComponent<DefaultAs>, 'defaultProps'>
> & {
  <As extends AsProp = DefaultAs>(
    props: Prefer<{as?: As} & Props, React.ComponentProps<As>> &
      React.RefAttributes<
        As extends keyof JSX.IntrinsicElements ? FromReactType<As> : As
      >
  ): JSX.Element | null
  defaultProps: {
    as?: AsProp
  } & Partial<Props> &
    Partial<React.ComponentPropsWithoutRef<DefaultAs>>
  displayName: string
}

PropsOf

/**
 * Extracts the props of a component type
 * Credit: Emotion
 */
export type PropsOf<
  E extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>
> = JSX.LibraryManagedAttributes<E, React.ComponentPropsWithRef<E>>

FromReactType

/**
 * Maps a keyof JSX.IntrinsicElement (e.g. 'div' or 'svg') or a
 * React.ComponentType to it's type.
 *
 * For example:
 *   FromReactType<"div"> ==> HTMLDivElement
 *   FromReactType<"svg"> ==> SVGSVGElement
 *   FromReactType<React.FC<P>. ==> React.FC<P>
 */
export type FromReactType<
  T extends React.ReactType
> = T extends keyof JSX.IntrinsicElements
  ? JSX.IntrinsicElements[T] extends React.DetailedHTMLFactory<
      React.HTMLAttributes<infer U>,
      infer U
    >
    ? U
    : JSX.IntrinsicElements[T] extends React.SVGProps<infer V>
    ? V
    : never
  : T

Prefer

/**
 * Omits an props in `T` that are already present in `P`
 */
export type Prefer<P, T> = P & Omit<T, keyof P>

LICENSE

MIT