J'ai cet exemple de composant
import React, { FC, ReactNode, useMemo } from "react";
import PropTypes from "prop-types";
type Props = {
children: ((x: number) => ReactNode) | ReactNode;
};
const Comp: FC<Props> = function Comp(props) {
const val = useMemo(() => {
return 1;
}, []);
return (
<div>
{typeof props.children === "function"
? props.children(val)
: props.children}
</div>
);
};
Comp.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired
};
export default Comp;
Mon intention ici est que le prop children
du composant puisse être soit
a node
, qui est décrit comme
Tout ce qui peut être rendu: des nombres, des chaînes, des éléments ou un tableau (ou fragment) contenant ces types.
un function
, (ou un "accessoire de rendu") qui obtient simplement une valeur de l'intérieur du composant et renvoie un autre node
le point ici est d'être explicite, que le children
peut être soit l'un (node
, qui est à peu près tout) soit l'autre (qui est simplement un function
)
Je suis cependant confronté aux problèmes suivants avec la vérification de type.
? props.children(val)
Cette expression n'est pas appelable. Tous les constituants de type "Fonction | ((x: nombre) => ReactNode) | (chaîne & {}) | (nombre & {}) | (faux & {}) | (vrai & {}) | ({} & chaîne) | ({} & nombre) | ({} & false) | ({} & vrai) | (((x: nombre) => ReactNode) & chaîne)
Je ne comprends pas cette erreur.
Props
pour êtretype Props = {
children: (x: number) => ReactNode;
};
et comptez sur le propre type PropsWithChildren<P> = P & { children?: ReactNode };
de React pour gérer le cas où children
n'est pas une fonction, alors j'obtiens l'erreur
(propriété) children ?: PropTypes.Validator <(x: number) => React.ReactNode> Le type 'Validator' n'est pas attribuable au type 'Validator <(x: number) => ReactNode>'. Le type 'ReactNodeLike' n'est pas assignable au type '(x: number) => ReactNode'. Le type 'string' n'est pas assignable au type '(x: number) => ReactNode'.ts (2322) Comp.tsx (5, 3): Le type attendu provient de la propriété' children 'qui est déclarée ici sur type
sur la ligne children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired
La seule solution est de laisser le type Props
comme
type Props = {
children: (x: number) => ReactNode;
};
et changez aussi le Comp.propTypes
en children: PropTypes.func.isRequired
, ce qui est pas ce que je veux, puisque je veux être explicite .
Comment puis-je garder le code explicite, tel que présenté au début de cette question, et ne pas avoir la vérification de type me renvoyer des erreurs?
Je pense que l'union mondiale pourrait aider:
type Props = {
children: ((x: number) => ReactNode);
} | {
children: ReactNode;
};
Une autre solution qui fonctionne, et qui ne nécessite pas d'écrire la déclaration Props
différemment ou de réécrire autre chose différemment, est de strictement définir le type de props
paramètre, lors de la définition du composant, comme ceci
type Props = {
children: ((x: number) => ReactNode) | ReactNode;
};
const Comp: FC<Props> = function Comp(props: Props) { // we strictly define the props type here
...
}
Comp.propTypes = {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired
};
Je ne suis pas sûr à 100% pourquoi cela fait une différence, mon intuition est que nous "forcons" notre propre Props
définition au vérificateur de type, donc nous limitons la portée possible.
Depuis que j'ai posé la question initiale, j'ai finalement opté pour la solution suivante à mon problème: J'ai défini mon propre type de composant de fonction :
//global.d.ts
declare module 'react' {
// Do not arbitrarily pass children down to props.
// Do not type check actual propTypes because they cannot always map 1:1 with TS types,
// forcing you to go with PropTypes.any very often, in order for the TS compiler
// to shut up
type CFC<P = {}> = CustomFunctionComponent<P>;
interface CustomFunctionComponent<P = {}> {
(props: P, context?: any): ReactElement | null;
propTypes?: { [key: string]: any };
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
}
Cette solution
children
arbitraire dans ma définitionComponent.propTypes
avec le TS type Props = {...}
. Souvent, ils ne mappaient pas exactement 1: 1, et j'ai été obligé d'utiliser PropTypes.any
ce n'est pas ce que je voulais.La raison pour laquelle je garde le Component.propTypes
avec les types TS, c'est que bien que TS soit très agréable pendant le développement, PropTypes
avertira en fait en cas de valeur de type incorrect pendant l'exécution, ce qui est un comportement utile lorsque, par exemple, un champ dans une réponse API était censée être un nombre et est maintenant une chaîne. Des choses comme celles-ci peuvent arriver et ce n'est pas quelque chose que TS peut aider.
https://github.com/DefinitelyTyped/DefinatelyTyped/issues/34237https://github.com/DefinitelyTyped/DefinitelyTyped/issues/34237#issuecomment-486374424