Skip to content

Multi-parameter React function components should be errors #33104

Closed
@danvk

Description

@danvk

React function components take a single props parameter. If you mistakenly think they take positional parameters, the error message you get can be quite confusing because it says nothing about the number of parameters. Instead, it's usually something to do with assignability.

Even if this is WAI, maybe this helps someone else with this error!

TypeScript Version: 3.5.2

Search Terms:

  • react function component error message

Code

import * as React from 'react';

function FC(a: any, b: number, c: number) {
  return <div>{a} {b} {c}</div>;
}

function MainComponent() {
  return <FC a="A" b="B" c="C" />
}

Expected behavior:

It should be an error to use a function which takes multiple parameters as a component in JSX. This is almost certainly a mistake. It's similar to calling a two-parameter function with one parameter, which is an error:

FC({a: 'A', b: 'B', c: 'C'});
// Expected 3 arguments, but got 1. ts(2554)

Actual behavior:

No error. b and c are undefined at runtime.

Playground Link: link

Related Issues:


Real-world example of the confusing way in which this came up:

import * as React from 'react';

interface RowType {
  [columnName: string]: string | number;
}

function CardComponent(props: {row: RowType; column: string; tooltip: string}): JSX.Element {
  return <div title={props.tooltip}>{props.row[props.column] || ''}</div>;
}

export class MyComponent extends React.Component<{row: RowType}, {}> {
  render() {
    const createCard = (col: string) => (
      <CardComponent
        row={this.props.row}
        key={col}
        column={col}
        tooltip={`This is column ${col}`}
      />
    );

    const els = ['A', 'B', 'C'].map((v, i) => createCard(v));

    return <div>{els}</div>;
  }
}

Refactor createCard to be a function component:

const FunctionCard = (row: RowType, column: string) => (
  <CardComponent row={row} column={column} tooltip={`This is column ${column}`} />
);

class MyComponent2 extends React.Component<{row: RowType}, {}> {
  render() {
    const createCard = (col: string) => (
      <FunctionCard row={this.props.row} key={col} column={col} />
    );

    const els = ['A', 'B', 'C'].map((v, i) => createCard(v));

    return <div>{els}</div>;
  }
}

You get the following error message on the row=:

Type 'RowType' is not assignable to type 'string | number'.
  Type 'RowType' is not assignable to type 'string'.ts(2322)
Toy.tsx(6, 3): The expected type comes from this index signature.

It's not clear at first glance why the type checker is trying to assign RowType to string | number. If you squint hard enough, you eventually realize that FunctionCard is getting called with a single argument:

FunctionCard({
  row: this.props.row,
  column: col
})

TypeScript tries to assign this {row, column} object to the first parameter of FunctionCard, namely row. Since this is RowType, which has an index signature, it tries to assign this.props.row (whose type is RowType) to the value type of the index signature, namely string | number. Hence the error.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions