skip to Main Content

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.

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">
            // ...

2

Answers


  1. import React from 'react';
    
    type Row = Record<string, any> & { key: string };
    type RowKeys = Exclude<keyof Row, 'key'>;
    
    type Props<RowType extends Row = Row> = {
      cellRenderer?: {
        [key in RowKeys]?: (row: RowType) => React.ReactNode;
      };
      columnOrder: Array<RowKeys>;
      columns: {
        [key in RowKeys]: string | React.ReactNode;
      };
      rows: Array<RowType>;
    };
    
    export const Table = <RowType extends Row = Row>({
      cellRenderer,
      columnOrder,
      columns,
      rows,
    }: Props<RowType>): React.ReactElement => {
      return (
        <div className="flow-root">
          <div className="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
            
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search