Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optional JSX Props In a TSX/JSX Project

I have a React project that I'm converting from JS to TS. An issue I'm running into is that TSX React is assuming that all properties defined in a functional component are required props.

// ComponentA.tsx
class ComponentA extends React.Component<any, any> {
  render() {
    /* Type '{ equalWidth: true; children: Element[]; }' is not assignable to type '{ children: any; className: any; equalWidth: any; }'.
     * Property 'className' is missing in type '{ equalWidth: true; children: Element[]; }'.' */
    return <ComponentB equalWidth />
  }
}

and

// ComponentB.js
const ComponentB = ({ children, className, equalWidth }) => {
  return (...)
}

is there a way to signal to TS that JSX component props are all optional?

like image 308
Tyler Sebastian Avatar asked Jul 31 '17 16:07

Tyler Sebastian


2 Answers

One simplest option will be setting a default value for your optional props. As an example, if className is optional you can change your ComponentB.js to something like this.

const ComponentB = ({ children, className="", equalWidth }) => {
  return (...)
}

Also if you deconstruct your props in the function body instead of the signature TS will not complain about typings.

const ComponentB = (props) => {
  const { children, className, equalWidth } = props;
  return (...)
}
like image 76
Tharaka Wijebandara Avatar answered Sep 17 '22 00:09

Tharaka Wijebandara


Assuming that ComponentB.js is going to end up as a TypeScript component:

interface ComponentBProps {
    children?: ReactNode;
    className?: string;
    equalWidth?: boolean;
}

const ComponentB = ({ children, className, equalWidth }: ComponentBProps) => {
    // 
};

In the special case where all properties are optional, you could remove the ? from each property on the interface and use Partial<ComponentBProps>, but I guess that at least something will end up being a required prop.


If you want to keep ComponentB.js as it is, then an alternative solution is to create a type definitions file:

import { ReactNode, StatelessComponent } from "react";

interface ComponentBProps {
    children?: ReactNode
    className?: string;
    equalWidth?: boolean;
}

export const ComponentB: StatelessComponent<ComponentBProps>;

If you put in this the same directory as the JavaScript file and name is ComponentB.d.ts, then you should be able to import ComponentB in your TypeScript file.

The way I have written the definition assumes that the component is a named export, not the default, i.e. it is exported like export const ComponentB in the .js file.

(probably) working example: https://github.com/fenech/tsx-jsx

like image 30
Tom Fenech Avatar answered Sep 17 '22 00:09

Tom Fenech