RSS Amplifier

Dylan Paulus' Blog · Jun 2, 2018

Generic Type Arguments in JSX Elements

0
Sign in to vote or save

This page did not load. You can still read it on the original site — the toolbar below keeps your place in the directory.

Typescript recently released generic type arguments for JSX in their 2.9 release. It's a mouthfull, but what does that mean for us? A common use-case I'm excited for is allowing consumers of libraries to extend a component's props. Using [dynamic components]({% post_url 2017-07-26-injecting-react-tag-types %}) we'll look at allowing our components to be extended even more. What Are Generic Type…

Typescript recently released generic type arguments for JSX in their 2.9 release. It's a mouthfull, but what does that mean for us? A common use-case I'm excited for is allowing consumers of libraries to extend a component's props. Using [dynamic components]({% post_url 2017-07-26-injecting-react-tag-types %}) we'll look at allowing our components to be extended even more.

What Are Generic Type Arguments?

As shown in the Typescript release notes, generic type arguments are a way to create components using Typescript's generics syntax. Below is a side-by-side comparison of the old way vs. using generic type arguments.

The Old Way:

// Notice color isn't defined as a prop, and will error out normally
function Div(props: { value: string }) {
  const { value, ...rest } = this.props;
  return <div {...rest} />;
}
// Using spread, we can trick Typescript into ignoring that color will be a prop
// on Div
function App() {
  return <Div {...{ color: "red" }} />;
}

Generic Type Arguments:

// Notice our new generic on the component
function Div<T extends object>(props: { value: string } & T) {
    const { value, ...rest } = props as any; // spreading on generics not yet supported
    return <div {...rest} />
}
interface IAdditionalProps {
    color: string;
}
// We can tell typescript our Div element has additional properties!
function App() {
    // Generic Type Arguments!
    return <Div<IAdditionalProps> color="red" value="TEXT!!!" />
}

And the same can be used with class components:

// Notice our new generic on the component
class Div<T extends object> extends React.Component<{ value: string } & T> {
    public render() {
        const { value, ...rest } = this.props as any;
        return <div {...rest} />
    }
}
interface IAdditionalProps {
    color: string;
}
// We can tell typescript our Div element has additional properties!
function App() {
    return <Div<IAdditionalProps> color="red" value="TEXT!!" />
}

Dynamic Elements

Let's say we have a MenuItem component that could be overloaded with either a Router link component, or a html a tag. One way we might write this...

interface IProps {
  tag: React.ReactNode;
  children: React.ReactNode;
}
function MenuItem({ tag, children, ...rest }: IProps) {
  const Tag: React.ReactType = tag || "a";
  return <Tag {...rest}>{children}</Tag>;
}

MenuItem works perfect fine as a component, but when it's time to add additional properties, Typescript will yell. For example, the a tag needs a href prop. We don't want to hardcode href, because we can inject any type of element through the tag prop (React Router, button, etc).

<MenuItem tag="a" href="http://google.com">Click Me!</MenuItem> // Error because href isn't defined in IProps!
<MenuItem tag={Link} to="/home">Home</MenuItem> // Error because to isn't defined in IProps!

We can fix our errors using generic type arguments.

interface IProps {
  tag: React.ReactNode;
  children: React.ReactNode;
}
function MenuItem<T extends object>(props: IProps & T) {
  const { tag, children, ...rest } = props as any;
  const Tag: React.ReactType = tag || "a";
  return (
      <Tag {...rest}>
          {children}
      </Tag>
  );
}

Now the consumer of our MenuItem component can tell us what additional properties are needed!

<MenuItem<{ href: string }> tag="a" href="http://google.com">Click Me!</MenuItem> // Success!
<MenuItem<{ to: string }> tag={Link} to="/home">Home</MenuItem> // Success!

Through generic type arguments for JSX, we are able to make our component more reusable. Users can extend components to allow additional props. Great!

Read on dylanpaulus.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.