@@ -100,7 +100,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
100
100
const defaults = { customValidators : [ ] } ;
101
101
const configuration = Object . assign ( { } , defaults , context . options [ 0 ] || { } ) ;
102
102
const customValidators = configuration . customValidators ;
103
- const allowedGenericTypes = new Set ( [ 'SFC' , 'StatelessComponent' , 'FunctionComponent' , 'FC' ] ) ;
103
+ const allowedGenericTypes = new Set ( [ 'PropsWithChildren' , ' SFC', 'StatelessComponent' , 'FunctionComponent' , 'FC' ] ) ;
104
104
const genericReactTypesImport = new Set ( ) ;
105
105
106
106
/**
@@ -496,6 +496,36 @@ module.exports = function propTypesInstructions(context, components, utils) {
496
496
return { } ;
497
497
}
498
498
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
+
499
529
class DeclarePropTypesForTSTypeAnnotation {
500
530
constructor ( propTypes , declaredPropTypes ) {
501
531
this . propTypes = propTypes ;
@@ -549,8 +579,13 @@ module.exports = function propTypesInstructions(context, components, utils) {
549
579
let typeName ;
550
580
if ( astUtil . isTSTypeReference ( node ) ) {
551
581
typeName = node . typeName . name ;
552
- const shouldTraverseTypeParams = ! typeName || genericReactTypesImport . has ( typeName ) ;
582
+ const shouldTraverseTypeParams = genericReactTypesImport . has ( getTypeName ( node . typeName ) ) ;
553
583
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
+
554
589
const nextNode = node . typeParameters . params [ 0 ] ;
555
590
this . visitTSNode ( nextNode ) ;
556
591
return ;
@@ -725,6 +760,14 @@ module.exports = function propTypesInstructions(context, components, utils) {
725
760
}
726
761
727
762
endAndStructDeclaredPropTypes ( ) {
763
+ if ( this . shouldSpecifyOptionalChildrenProps ) {
764
+ this . declaredPropTypes . children = {
765
+ fullName : 'children' ,
766
+ name : 'children' ,
767
+ node : { } ,
768
+ isRequired : false
769
+ } ;
770
+ }
728
771
this . foundDeclaredPropertiesList . forEach ( ( tsInterfaceBody ) => {
729
772
if ( tsInterfaceBody && ( tsInterfaceBody . type === 'TSPropertySignature' || tsInterfaceBody . type === 'TSMethodSignature' ) ) {
730
773
let accessor = 'name' ;
@@ -928,6 +971,16 @@ module.exports = function propTypesInstructions(context, components, utils) {
928
971
}
929
972
} ) ;
930
973
} 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
+ }
931
984
markPropTypesAsDeclared ( node , resolveTypeAnnotation ( param ) ) ;
932
985
}
933
986
} else {
@@ -942,21 +995,8 @@ module.exports = function propTypesInstructions(context, components, utils) {
942
995
return ;
943
996
}
944
997
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 ;
954
999
955
- if ( ! genericReactTypesImport . has ( left ) || ! allowedGenericTypes . has ( right ) ) {
956
- return ;
957
- }
958
- }
959
- }
960
1000
markPropTypesAsDeclared ( node , resolveTypeAnnotation ( siblingIdentifier ) ) ;
961
1001
}
962
1002
}
0 commit comments