Skip to content

How do I wrap my Polymorphic Component? #8

@mrbinky3000

Description

@mrbinky3000

Thank you for your excellent tutorial. I'm still new to Typescript.

I'm not sure how to write a component that wraps my Polymorphic created using your tutorial. Lets call the polymorphic component "Atom" for the sake of this question.

The wrapper component is called "Molecule" and I want "Molecule" to accept have all the same props as "Atom" plus a few more.

Should my wrapper component also use your Polymorphic types to take advantage of the strict typing? That's what I'm doing right now. There is a small problem, though. I can't pass the "as" prop from Molecule to Atom.

As an example, I've copied your example code for the Text component and also added a Wrapper component that wraps Text and provides an additional style. I can't use Wrapper to pass the "as" prop to the Text component.

import React from 'react';

type AsProp<C extends React.ElementType> = { as?: C };

type PropsToOmit<C extends React.ElementType, P> = keyof (AsProp<C> & P);

// This is the first reusable type utility we built
type PolymorphicComponentProp<
  C extends React.ElementType,
  Props = object,
> = React.PropsWithChildren<Props & AsProp<C>> &
  Omit<React.ComponentPropsWithoutRef<C>, PropsToOmit<C, Props>>;

// This is a new type utility with ref!
type PolymorphicComponentPropWithRef<
  C extends React.ElementType,
  Props = object,
> = PolymorphicComponentProp<C, Props> & { ref?: PolymorphicRef<C> };

// This is the type for the "ref" only
type PolymorphicRef<C extends React.ElementType> =
  React.ComponentPropsWithRef<C>['ref'];

// -----------------------------------------------------------------------------
// Text Component
// -----------------------------------------------------------------------------

type Rainbow =
  | 'red'
  | 'orange'
  | 'yellow'
  | 'green'
  | 'blue'
  | 'indigo'
  | 'violet';

/**
 * This is the updated component props using PolymorphicComponentPropWithRef
 **/
type TextProps<C extends React.ElementType> = PolymorphicComponentPropWithRef<
  C,
  { color?: Rainbow | 'black' }
>;

/**
 * This is the type used in the type annotation for the component
 **/
type TextComponent = <C extends React.ElementType = 'span'>(
  props: TextProps<C>
) => React.ReactNode | null;

export const Text: TextComponent = React.forwardRef(function Text<
  C extends React.ElementType = 'span',
>({ as, color, children }: TextProps<C>, ref?: PolymorphicRef<C>) {
  const Component = as ?? 'span';
  const style = color ? { style: { color } } : {};
  return (
    <Component {...style} ref={ref}>
      {' '}
      {children}{' '}
    </Component>
  );
});

// -----------------------------------------------------------------------------
// Wrapper Component
// -----------------------------------------------------------------------------

/**
 * This is the updated component props using PolymorphicComponentPropWithRef
 **/
type WrapperProps<C extends React.ElementType> =
  PolymorphicComponentPropWithRef<
    C,
    {
      color?: Rainbow | 'black';
      size?: 'big' | 'small';
    }
  >;

/**
 * This is the type used in the type annotation for the component
 **/
type WrapperComponent = <C extends React.ElementType = 'span'>(
  props: WrapperProps<C>
) => React.ReactNode | null;

export const Wrapper: WrapperComponent = React.forwardRef(function Text<
  C extends React.ElementType = 'span',
>(
  { as, color, children, size = 'small' }: WrapperProps<C>,
  ref?: PolymorphicRef<C>
) {
  const style = {
    style: {
      color,
      fontSize: size === 'small' ? '10px' : '40px',
    },
  };
  return (
    <Text 
      as={as}  // 👈 This causes a typeerror in Text.  If I set it to as='div' it works. But I want wrapper to be able to specify a tag.  How do we make as a string?
      {...style} 
      ref={ref}
    >
      {children}
    </Text>
  );
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions