@@ -100,7 +100,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
100100 const defaults = { customValidators : [ ] } ;
101101 const configuration = Object . assign ( { } , defaults , context . options [ 0 ] || { } ) ;
102102 const customValidators = configuration . customValidators ;
103- const allowedGenericTypes = new Set ( [ 'SFC' , 'StatelessComponent' , 'FunctionComponent' , 'FC' ] ) ;
103+ const allowedGenericTypes = new Set ( [ 'PropsWithChildren' , ' SFC', 'StatelessComponent' , 'FunctionComponent' , 'FC' ] ) ;
104104 const genericReactTypesImport = new Set ( ) ;
105105
106106 /**
@@ -496,6 +496,36 @@ module.exports = function propTypesInstructions(context, components, utils) {
496496 return { } ;
497497 }
498498
499+ function isValidReactGenericTypeAnnotation ( annotation ) {
500+ if ( annotation . typeName ) {
501+ if ( annotation . typeName . name ) { // if FC<Props>
502+ const typeName = annotation . typeName . name ;
503+ if ( ! genericReactTypesImport . has ( typeName ) ) {
504+ return false ;
505+ }
506+ } else if ( annotation . typeName . right . name ) { // if React.FC<Props>
507+ const right = annotation . typeName . right . name ;
508+ const left = annotation . typeName . left . name ;
509+
510+ if ( ! genericReactTypesImport . has ( left ) || ! allowedGenericTypes . has ( right ) ) {
511+ return false ;
512+ }
513+ }
514+ }
515+ return true ;
516+ }
517+
518+ /**
519+ * Returns the left most typeName of a node, e.g: FC<Props>, React.FC<Props>
520+ * The representation is used to verify nested used properties.
521+ * @param {ASTNode } node
522+ * @return {string | undefined }
523+ */
524+ function getTypeName ( node ) {
525+ if ( node . name ) return node . name ;
526+ if ( node . left ) return getTypeName ( node . left ) ;
527+ }
528+
499529 class DeclarePropTypesForTSTypeAnnotation {
500530 constructor ( propTypes , declaredPropTypes ) {
501531 this . propTypes = propTypes ;
@@ -549,8 +579,13 @@ module.exports = function propTypesInstructions(context, components, utils) {
549579 let typeName ;
550580 if ( astUtil . isTSTypeReference ( node ) ) {
551581 typeName = node . typeName . name ;
552- const shouldTraverseTypeParams = ! typeName || genericReactTypesImport . has ( typeName ) ;
582+ const shouldTraverseTypeParams = genericReactTypesImport . has ( getTypeName ( node . typeName ) ) ;
553583 if ( shouldTraverseTypeParams && node . typeParameters && node . typeParameters . length !== 0 ) {
584+ // All react Generic types are derived from:
585+ // type PropsWithChildren<P> = P & { children?: ReactNode | undefined }
586+ // So we should construct an optional children prop
587+ this . shouldSpecifyOptionalChildrenProps = true ;
588+
554589 const nextNode = node . typeParameters . params [ 0 ] ;
555590 this . visitTSNode ( nextNode ) ;
556591 return ;
@@ -725,6 +760,14 @@ module.exports = function propTypesInstructions(context, components, utils) {
725760 }
726761
727762 endAndStructDeclaredPropTypes ( ) {
763+ if ( this . shouldSpecifyOptionalChildrenProps ) {
764+ this . declaredPropTypes . children = {
765+ fullName : 'children' ,
766+ name : 'children' ,
767+ node : { } ,
768+ isRequired : false
769+ } ;
770+ }
728771 this . foundDeclaredPropertiesList . forEach ( ( tsInterfaceBody ) => {
729772 if ( tsInterfaceBody && ( tsInterfaceBody . type === 'TSPropertySignature' || tsInterfaceBody . type === 'TSMethodSignature' ) ) {
730773 let accessor = 'name' ;
@@ -928,6 +971,16 @@ module.exports = function propTypesInstructions(context, components, utils) {
928971 }
929972 } ) ;
930973 } else {
974+ // check if its a valid generic type when `X<{...}>`
975+ if (
976+ param . typeAnnotation
977+ && param . typeAnnotation . typeAnnotation
978+ && param . typeAnnotation . typeAnnotation . type === 'TSTypeReference'
979+ && param . typeAnnotation . typeAnnotation . typeParameters != null
980+ && ! isValidReactGenericTypeAnnotation ( param . typeAnnotation . typeAnnotation )
981+ ) {
982+ return ;
983+ }
931984 markPropTypesAsDeclared ( node , resolveTypeAnnotation ( param ) ) ;
932985 }
933986 } else {
@@ -942,21 +995,8 @@ module.exports = function propTypesInstructions(context, components, utils) {
942995 return ;
943996 }
944997
945- if ( annotation . typeName ) {
946- if ( annotation . typeName . name ) { // if FC<Props>
947- const typeName = annotation . typeName . name ;
948- if ( ! genericReactTypesImport . has ( typeName ) ) {
949- return ;
950- }
951- } else if ( annotation . typeName . right . name ) { // if React.FC<Props>
952- const right = annotation . typeName . right . name ;
953- const left = annotation . typeName . left . name ;
998+ if ( ! isValidReactGenericTypeAnnotation ( annotation ) ) return ;
954999
955- if ( ! genericReactTypesImport . has ( left ) || ! allowedGenericTypes . has ( right ) ) {
956- return ;
957- }
958- }
959- }
9601000 markPropTypesAsDeclared ( node , resolveTypeAnnotation ( siblingIdentifier ) ) ;
9611001 }
9621002 }
0 commit comments