Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript React component infer one prop from another

I could use some help defining the type for my element’s Props, as I’m convinced that what I’m trying to achieve is possible, but I just cannot get it to work.

In my TS app I have a component, that takes the following properties:

  • rows: an array of objects, with dynamic properties, plus a required key property. It doesn’t matter what these properties are, but these rows should all have the exact same set of properties.
  • columnOrder: an array with (preferably all) properties of the row’s type, to indicate in which order they should be rendered.
  • columns: an object where every property of the row’s type is an optional property that maps to a string (the column’s headers)
  • cellRenderer: similar to columns it should allow all properties in the row’s type to map to a render function (which takes the row as an input variable).

This is what I’ve currently got, but it’s way too loose, I can pass properties that aren’t in the rows to the columns, columnOrder, and cellRenderer props and I don’t want to be able to.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React from 'react';

type Props<
    Row extends Record<string, any> & { key: string } = Record<string, any> & { key: string }
> = {
    cellRenderer?: {
        [key in keyof Omit<Row, 'key'>]?: (row: Row) => React.JSX.Element;
    }
    columnOrder: Array<keyof Omit<Row, 'key'>>;
    columns: {
        [key in keyof Omit<Row, 'key'>]: string | React.JSX.Element
    };
    rows: Array<Row>;
}

export const Table = ({ cellRenderer, columnOrder, columns, rows }: Props): React.JSX.Element => {
    return <div className="flow-root">
        <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
            // ...

>Solution :

Based on your requirements, it seems like you want to define a strict type for your component’s props to ensure that only properties from the Row type can be passed to the columns, columnOrder, and cellRenderer props. To achieve this, you can make use of mapped types and conditional types in TypeScript.

import React from 'react';

type Props<Row extends Record<string, any> & { key: string }> = {
  cellRenderer?: {
    [Key in keyof Omit<Row, 'key'>]?: (row: Row) => React.ReactNode;
  };
  columnOrder: Array<keyof Omit<Row, 'key'>>;
  columns: {
    [Key in keyof Omit<Row, 'key'>]: string | React.ReactNode;
  };
  rows: Array<Row>;
};

export const Table = <Row extends Record<string, any> & { key: string }>({
  cellRenderer,
  columnOrder,
  columns,
  rows,
}: Props<Row>): React.ReactNode => {
  return (
    <div className="flow-root">
      <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
        {/* ... */}
      </div>
    </div>
  );
};

In this:

The Props type is defined with a generic parameter Row, which represents the type of the rows in the table.
The cellRenderer prop is defined with a mapped type that only allows keys from Row excluding the ‘key’ property. The values of this prop are functions that take a Row and return a React.ReactNode.
The columnOrder prop is defined as an array of keys from Row excluding the ‘key’ property.
The columns prop is defined with a mapped type similar to cellRenderer, but the values can be either strings or React.ReactNode.
The rows prop is simply an array of Row objects.
With these type definitions, TypeScript will enforce that you can only pass properties from Row (excluding the ‘key’ property) to the columns, columnOrder, and cellRenderer props.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading