Description
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.