reactjs - React Generic Props erhält einen Fehler beim Verwenden einer Funktion in Props
Ich habe einen grundlegenden Codeausschnitt, bei dem die Props generisch sind, und ich versuche, eine Funktion bereitzustellen, die bereits vom generischen Typ abhängig ist. Bei Nicht-Funktionen funktioniert alles gut, aber wenn ich versuche, eine Funktion in den Props bereitzustellen, bekomme ich einen Fehler.
import React from 'react';
import { concatClasses } from '@/utils/component';
import type { IOption } from '../interfaces/option';
import classes from './Filter.module.scss';
interface IProps<T> {
readonly title: string;
readonly options: IOption<T>[];
readonly selectedOption: T;
readonly onSelectOption: (value: T) => void;
}
const FilterView = <T,>(props: React.PropsWithChildren<IProps<T>>) => {
return (
<div className={classes['container']}>
<h5 className={classes['container__title']}>{props.title}</h5>
{props.options.map((option, index) => (
<button
key={index}
className={concatClasses(
classes,
'container__option',
props.selectedOption === option.value ? 'container__option--selected' : null,
)}
type="button"
onClick={() => props.onSelectOption(option.value)}
>
{option.label}
</button>
))}
</div>
);
};
FilterView.displayName = 'FilterView';
FilterView.defaultProps = {};
export default React.memo(FilterView);
Hier ist alles in Ordnung, aber dann versuche ich, diese Komponente zu verwenden, und bekomme einen Fehler:
import React from 'react';
import type { IOption } from '../interfaces/option';
import FilterView from './Filter.view';
interface IProps<T> {
readonly title: string;
readonly options: IOption<T>[];
readonly selectedOption: T;
readonly onSelectOption: (value: T) => void;
}
const Filter = <T,>(props: React.PropsWithChildren<IProps<T>>) => (
<FilterView
title={props.title}
options={props.options}
selectedOption={props.selectedOption}
onSelectOption={props.onSelectOption}
/>
);
Filter.displayName = 'Filter';
Filter.defaultProps = {};
export default React.memo(Filter);
Der Fehler, den ich erhalte, ist in dieser Zeile markiert onSelectOption={props.onSelectOption}:
(property) onSelectOption: (value: unknown) => void
Type '(value: T) => void' is not assignable to type '(value: unknown) => void'.
Types of parameters 'value' and 'value' are incompatible.
Type 'unknown' is not assignable to type 'T'.
'T' could be instantiated with an arbitrary type which could be unrelated to 'unknown'.ts(2322)
Könnte jemand sagen, warum?
Die IOption-Schnittstelle:
export interface IOption<T> {
readonly value: T;
readonly label: string;
}
Antwort #1
Dies ist leider eine bekannte Einschränkung des React.memo-Typs: Er behandelt die Generika von Komponenten nicht.
Siehe https://newdevzone.com/posts/how-to-use-props-with-generics-with-reactmemo
Eine einfache Lösung besteht darin, diesen Typ mit einer Überladung zu erweitern:
declare module "react" { // augment React types
function memo<A, B>(Component: (props: A) => B): (props: A) => React.ReactElement | null
// return type is same as ReturnType<ExoticComponent<any>>
}
const Filter = <T,>(props: React.PropsWithChildren<IProps<T>>) => (
<FilterView
title={props.title}
options={props.options}
selectedOption={props.selectedOption}
onSelectOption={props.onSelectOption} // Okay
/>
);
Spielplatz-Link
Antwort #2
Wenn Sie den Typ nicht erweitern möchten, können Sie TypeScript einfach sagen, dass die exportierte Komponente der Typ ist, den Sie möchten.
export default React.memo(FilterView) as typeof FilterView;
