flow-api-translator 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,24 +11,32 @@
11
11
  'use strict';
12
12
 
13
13
  import type {ObjectWithLoc} from 'hermes-estree';
14
- import * as FlowESTree from 'hermes-estree';
14
+ import type {TranslationOptions} from './utils/TranslationUtils';
15
15
  import type {ScopeManager} from 'hermes-eslint';
16
+
17
+ import * as FlowESTree from 'hermes-estree';
18
+ import * as TSESTree from './utils/ts-estree-ast-types';
16
19
  import {
17
20
  cloneJSDocCommentsToNewNode as cloneJSDocCommentsToNewNodeOriginal,
18
21
  makeCommentOwnLine as makeCommentOwnLineOriginal,
19
22
  } from 'hermes-transform';
20
- import * as TSESTree from './utils/ts-estree-ast-types';
21
23
  import {
22
24
  buildCodeFrame,
23
25
  translationError as translationErrorBase,
24
26
  unexpectedTranslationError as unexpectedTranslationErrorBase,
25
27
  } from './utils/ErrorUtils';
26
28
  import {removeAtFlowFromDocblock} from './utils/DocblockUtils';
27
- import type {TranslationOptions} from './utils/TranslationUtils';
28
29
  import {EOL} from 'os';
29
30
 
30
31
  type DeclarationOrUnsupported<T> = T | TSESTree.TSTypeAliasDeclaration;
31
32
 
33
+ function constructFlowNode<T: FlowESTree.BaseNode>(
34
+ node: $Diff<T, FlowESTree.BaseNode>,
35
+ ): T {
36
+ // $FlowFixMe[prop-missing]
37
+ return node;
38
+ }
39
+
32
40
  const cloneJSDocCommentsToNewNode =
33
41
  // $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
34
42
  (cloneJSDocCommentsToNewNodeOriginal: (mixed, mixed) => void);
@@ -45,6 +53,19 @@ function isValidReactImportOrGlobal(id: FlowESTree.Identifier): boolean {
45
53
 
46
54
  let shouldAddReactImport: boolean | null = null;
47
55
 
56
+ // Returns appropriate Identifier for `React` import.
57
+ // If a global is in use, set a flag to indicate that we should add the import.
58
+ function getReactIdentifier(hasReactImport: boolean) {
59
+ if (shouldAddReactImport !== false) {
60
+ shouldAddReactImport = !hasReactImport;
61
+ }
62
+
63
+ return {
64
+ type: 'Identifier',
65
+ name: `React`,
66
+ };
67
+ }
68
+
48
69
  export function flowDefToTSDef(
49
70
  originalCode: string,
50
71
  ast: FlowESTree.Program,
@@ -204,10 +225,10 @@ const getTransforms = (
204
225
  return globalScope;
205
226
  })();
206
227
 
207
- function isReactImport(id: FlowESTree.Identifier): boolean {
228
+ function isReactImport(scopeNode: FlowESTree.ESNode, name: string): boolean {
208
229
  let currentScope = (() => {
209
230
  let scope = null;
210
- let node: FlowESTree.ESNode = id;
231
+ let node: FlowESTree.ESNode = scopeNode;
211
232
  while (!scope && node) {
212
233
  scope = scopeManager.acquire(node, true);
213
234
  node = node.parent;
@@ -223,7 +244,7 @@ const getTransforms = (
223
244
  const variableDef = (() => {
224
245
  while (currentScope != null) {
225
246
  for (const variable of currentScope.variables) {
226
- if (variable.defs.length && variable.name === id.name) {
247
+ if (variable.defs.length && variable.name === name) {
227
248
  return variable;
228
249
  }
229
250
  }
@@ -254,7 +275,7 @@ const getTransforms = (
254
275
 
255
276
  // Globals
256
277
  case 'ImplicitGlobalVariable': {
257
- return VALID_REACT_IMPORTS.has(id.name);
278
+ return VALID_REACT_IMPORTS.has(name);
258
279
  }
259
280
 
260
281
  // TODO Handle:
@@ -657,7 +678,17 @@ const getTransforms = (
657
678
  case 'VoidTypeAnnotation':
658
679
  return transform.VoidTypeAnnotation(node);
659
680
  case 'TypePredicate':
660
- return unsupportedAnnotation(node, node.type);
681
+ return transform.TypePredicateAnnotation(node);
682
+ case 'ConditionalTypeAnnotation':
683
+ return transform.ConditionalTypeAnnotation(node);
684
+ case 'InferTypeAnnotation':
685
+ return transform.InferTypeAnnotation(node);
686
+ case 'KeyofTypeAnnotation':
687
+ return transform.KeyofTypeAnnotation(node);
688
+ case 'TypeOperator':
689
+ return transform.TypeOperator(node);
690
+ case 'ComponentTypeAnnotation':
691
+ return transform.ComponentTypeAnnotation(node);
661
692
  default:
662
693
  throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
663
694
  }
@@ -986,6 +1017,23 @@ const getTransforms = (
986
1017
  ];
987
1018
  }
988
1019
 
1020
+ // TS doesn't support direct default export for declare'd functions
1021
+ case 'DeclareComponent': {
1022
+ const functionDecl = transform.DeclareComponent(declaration);
1023
+ const name = declaration.id.name;
1024
+ return [
1025
+ functionDecl,
1026
+ {
1027
+ type: 'ExportDefaultDeclaration',
1028
+ declaration: {
1029
+ type: 'Identifier',
1030
+ name,
1031
+ },
1032
+ exportKind: 'value',
1033
+ },
1034
+ ];
1035
+ }
1036
+
989
1037
  // Flow's declare export default Identifier is ambiguous.
990
1038
  // the Identifier might reference a type, or it might reference a value
991
1039
  // - If it's a value, then that's all good, TS supports that.
@@ -1122,6 +1170,13 @@ const getTransforms = (
1122
1170
  exportKind: 'value',
1123
1171
  },
1124
1172
  ];
1173
+ case 'DeclareComponent':
1174
+ return [
1175
+ {
1176
+ declaration: transform.DeclareComponent(node.declaration),
1177
+ exportKind: 'value',
1178
+ },
1179
+ ];
1125
1180
  case 'DeclareFunction':
1126
1181
  return [
1127
1182
  {
@@ -1194,6 +1249,134 @@ const getTransforms = (
1194
1249
  }
1195
1250
  }
1196
1251
  },
1252
+ DeclareComponent(
1253
+ node: FlowESTree.DeclareComponent,
1254
+ ): TSESTree.TSDeclareFunction {
1255
+ const id = transform.Identifier(node.id, false);
1256
+
1257
+ const typeParameters =
1258
+ node.typeParameters == null
1259
+ ? undefined
1260
+ : transform.TypeParameterDeclaration(node.typeParameters);
1261
+
1262
+ const params = transform.ComponentTypeParameters(node.params, node.rest);
1263
+
1264
+ // TS cannot support `renderType` so we always use ReactNode as the return type.
1265
+ const hasReactImport = isReactImport(node, 'React');
1266
+ const returnType = {
1267
+ type: 'TSTypeAnnotation',
1268
+ // If no rendersType we assume its ReactNode type.
1269
+ typeAnnotation: {
1270
+ type: 'TSTypeReference',
1271
+ typeName: {
1272
+ type: 'TSQualifiedName',
1273
+ left: getReactIdentifier(hasReactImport),
1274
+ right: {
1275
+ type: 'Identifier',
1276
+ name: `ReactNode`,
1277
+ },
1278
+ },
1279
+ typeParameters: undefined,
1280
+ },
1281
+ };
1282
+
1283
+ return {
1284
+ type: 'TSDeclareFunction',
1285
+ async: false,
1286
+ body: undefined,
1287
+ declare: true,
1288
+ expression: false,
1289
+ generator: false,
1290
+ id: {
1291
+ type: 'Identifier',
1292
+ name: id.name,
1293
+ },
1294
+ params,
1295
+ returnType: returnType,
1296
+ typeParameters: typeParameters,
1297
+ };
1298
+ },
1299
+ ComponentTypeParameters(
1300
+ params: $ReadOnlyArray<FlowESTree.ComponentTypeParameter>,
1301
+ rest: FlowESTree.ComponentTypeParameter | null,
1302
+ ): $ReadOnlyArray<TSESTree.Parameter> {
1303
+ if (params.length === 0 && rest != null) {
1304
+ return [
1305
+ {
1306
+ type: 'Identifier',
1307
+ name: 'props',
1308
+ typeAnnotation: {
1309
+ type: 'TSTypeAnnotation',
1310
+ typeAnnotation: transformTypeAnnotationType(rest.typeAnnotation),
1311
+ },
1312
+ optional: false,
1313
+ },
1314
+ ];
1315
+ }
1316
+
1317
+ const flowPropsType: Array<
1318
+ FlowESTree.ObjectTypeProperty | FlowESTree.ObjectTypeSpreadProperty,
1319
+ > = [];
1320
+
1321
+ if (rest != null) {
1322
+ flowPropsType.push(
1323
+ constructFlowNode<FlowESTree.ObjectTypeSpreadProperty>({
1324
+ type: 'ObjectTypeSpreadProperty',
1325
+ argument: rest.typeAnnotation,
1326
+ range: rest.range,
1327
+ loc: rest.loc,
1328
+ }),
1329
+ );
1330
+ }
1331
+
1332
+ for (let i = 0; i < params.length; i++) {
1333
+ const param = params[i];
1334
+ flowPropsType.push(
1335
+ constructFlowNode<FlowESTree.ObjectTypePropertySignature>({
1336
+ type: 'ObjectTypeProperty',
1337
+ kind: 'init',
1338
+ method: false,
1339
+ optional: param.optional,
1340
+ variance: null,
1341
+ proto: false,
1342
+ static: false,
1343
+ key:
1344
+ param.name ??
1345
+ constructFlowNode<FlowESTree.Identifier>({
1346
+ type: 'Identifier',
1347
+ name: `$$PARAM_${i}$$`,
1348
+ optional: false,
1349
+ typeAnnotation: null,
1350
+ }),
1351
+ value: param.typeAnnotation,
1352
+ range: param.range,
1353
+ loc: param.loc,
1354
+ }),
1355
+ );
1356
+ }
1357
+ const tsPropsObjectType = transform.ObjectTypeAnnotation(
1358
+ constructFlowNode<FlowESTree.ObjectTypeAnnotation>({
1359
+ type: 'ObjectTypeAnnotation',
1360
+ inexact: false,
1361
+ exact: true,
1362
+ properties: flowPropsType,
1363
+ indexers: [],
1364
+ callProperties: [],
1365
+ internalSlots: [],
1366
+ }),
1367
+ );
1368
+ return [
1369
+ {
1370
+ type: 'Identifier',
1371
+ name: 'props',
1372
+ typeAnnotation: {
1373
+ type: 'TSTypeAnnotation',
1374
+ typeAnnotation: tsPropsObjectType,
1375
+ },
1376
+ optional: false,
1377
+ },
1378
+ ];
1379
+ },
1197
1380
  DeclareFunction(
1198
1381
  node: FlowESTree.DeclareFunction,
1199
1382
  ): TSESTree.TSDeclareFunction {
@@ -1657,7 +1840,7 @@ const getTransforms = (
1657
1840
  return {
1658
1841
  type: 'TSImportType',
1659
1842
  isTypeOf: true,
1660
- parameter: moduleName,
1843
+ argument: moduleName,
1661
1844
  qualifier: null,
1662
1845
  typeParameters: null,
1663
1846
  };
@@ -1757,6 +1940,34 @@ const getTransforms = (
1757
1940
  };
1758
1941
  }
1759
1942
 
1943
+ case '$ReadOnlyMap': {
1944
+ return {
1945
+ type: 'TSTypeReference',
1946
+ typeName: {
1947
+ type: 'Identifier',
1948
+ name: 'ReadonlyMap',
1949
+ },
1950
+ typeParameters: {
1951
+ type: 'TSTypeParameterInstantiation',
1952
+ params: assertHasExactlyNTypeParameters(1),
1953
+ },
1954
+ };
1955
+ }
1956
+
1957
+ case '$ReadOnlySet': {
1958
+ return {
1959
+ type: 'TSTypeReference',
1960
+ typeName: {
1961
+ type: 'Identifier',
1962
+ name: 'ReadonlySet',
1963
+ },
1964
+ typeParameters: {
1965
+ type: 'TSTypeParameterInstantiation',
1966
+ params: assertHasExactlyNTypeParameters(1),
1967
+ },
1968
+ };
1969
+ }
1970
+
1760
1971
  case '$Values': {
1761
1972
  // `$Values<T>` => `T[keyof T]`
1762
1973
  const transformedType = assertHasExactlyNTypeParameters(1)[0];
@@ -1812,21 +2023,8 @@ const getTransforms = (
1812
2023
 
1813
2024
  // React special conversion:
1814
2025
  const validReactImportOrGlobal = isValidReactImportOrGlobal(baseId);
1815
- const reactImport = isReactImport(baseId);
1816
- if (validReactImportOrGlobal || reactImport) {
1817
- // Returns appropriate Identifier for `React` import.
1818
- // If a global is in use, set a flag to indicate that we should add the import.
1819
- const getReactIdentifier = () => {
1820
- if (shouldAddReactImport !== false) {
1821
- shouldAddReactImport = !reactImport;
1822
- }
1823
-
1824
- return {
1825
- type: 'Identifier',
1826
- name: `React`,
1827
- };
1828
- };
1829
-
2026
+ const hasReactImport = isReactImport(baseId, baseId.name);
2027
+ if (validReactImportOrGlobal || hasReactImport) {
1830
2028
  switch (fullTypeName) {
1831
2029
  // TODO: In flow this is `ChildrenArray<T> = T | $ReadOnlyArray<ChildrenArray<T>>`.
1832
2030
  // The recursive nature of it is rarely needed, so we're simplifying this for now
@@ -1878,7 +2076,7 @@ const getTransforms = (
1878
2076
  type: 'TSTypeReference',
1879
2077
  typeName: {
1880
2078
  type: 'TSQualifiedName',
1881
- left: getReactIdentifier(),
2079
+ left: getReactIdentifier(hasReactImport),
1882
2080
  right: {
1883
2081
  type: 'Identifier',
1884
2082
  name: 'Component',
@@ -1899,7 +2097,7 @@ const getTransforms = (
1899
2097
  type: 'TSTypeReference',
1900
2098
  typeName: {
1901
2099
  type: 'TSQualifiedName',
1902
- left: getReactIdentifier(),
2100
+ left: getReactIdentifier(hasReactImport),
1903
2101
  right: {
1904
2102
  type: 'Identifier',
1905
2103
  name: `Context`,
@@ -1919,7 +2117,7 @@ const getTransforms = (
1919
2117
  type: 'TSTypeReference',
1920
2118
  typeName: {
1921
2119
  type: 'TSQualifiedName',
1922
- left: getReactIdentifier(),
2120
+ left: getReactIdentifier(hasReactImport),
1923
2121
  right: {
1924
2122
  type: 'Identifier',
1925
2123
  name: 'Key',
@@ -1935,7 +2133,7 @@ const getTransforms = (
1935
2133
  type: 'TSTypeReference',
1936
2134
  typeName: {
1937
2135
  type: 'TSQualifiedName',
1938
- left: getReactIdentifier(),
2136
+ left: getReactIdentifier(hasReactImport),
1939
2137
  right: {
1940
2138
  type: 'Identifier',
1941
2139
  name: `ElementType`,
@@ -1952,7 +2150,7 @@ const getTransforms = (
1952
2150
  type: 'TSTypeReference',
1953
2151
  typeName: {
1954
2152
  type: 'TSQualifiedName',
1955
- left: getReactIdentifier(),
2153
+ left: getReactIdentifier(hasReactImport),
1956
2154
  right: {
1957
2155
  type: 'Identifier',
1958
2156
  name: `ReactNode`,
@@ -1968,7 +2166,7 @@ const getTransforms = (
1968
2166
  type: 'TSTypeReference',
1969
2167
  typeName: {
1970
2168
  type: 'TSQualifiedName',
1971
- left: getReactIdentifier(),
2169
+ left: getReactIdentifier(hasReactImport),
1972
2170
  right: {
1973
2171
  type: 'Identifier',
1974
2172
  name: `ReactElement`,
@@ -1988,7 +2186,7 @@ const getTransforms = (
1988
2186
  type: 'TSTypeReference',
1989
2187
  typeName: {
1990
2188
  type: 'TSQualifiedName',
1991
- left: getReactIdentifier(),
2189
+ left: getReactIdentifier(hasReactImport),
1992
2190
  right: {
1993
2191
  type: 'Identifier',
1994
2192
  name: `ElementRef`,
@@ -2008,7 +2206,7 @@ const getTransforms = (
2008
2206
  type: 'TSTypeReference',
2009
2207
  typeName: {
2010
2208
  type: 'TSQualifiedName',
2011
- left: getReactIdentifier(),
2209
+ left: getReactIdentifier(hasReactImport),
2012
2210
  right: {
2013
2211
  type: 'Identifier',
2014
2212
  name: `Fragment`,
@@ -2043,7 +2241,7 @@ const getTransforms = (
2043
2241
  type: 'TSTypeReference',
2044
2242
  typeName: {
2045
2243
  type: 'TSQualifiedName',
2046
- left: getReactIdentifier(),
2244
+ left: getReactIdentifier(hasReactImport),
2047
2245
  right: {
2048
2246
  type: 'Identifier',
2049
2247
  name: 'ComponentType',
@@ -2115,7 +2313,7 @@ const getTransforms = (
2115
2313
  type: 'TSTypeReference',
2116
2314
  typeName: {
2117
2315
  type: 'TSQualifiedName',
2118
- left: getReactIdentifier(),
2316
+ left: getReactIdentifier(hasReactImport),
2119
2317
  right: {
2120
2318
  type: 'Identifier',
2121
2319
  name: 'ComponentType',
@@ -2135,7 +2333,7 @@ const getTransforms = (
2135
2333
  type: 'TSTypeReference',
2136
2334
  typeName: {
2137
2335
  type: 'TSQualifiedName',
2138
- left: getReactIdentifier(),
2336
+ left: getReactIdentifier(hasReactImport),
2139
2337
  right: {
2140
2338
  type: 'Identifier',
2141
2339
  name: 'ComponentProps',
@@ -2173,7 +2371,7 @@ const getTransforms = (
2173
2371
  type: 'TSTypeReference',
2174
2372
  typeName: {
2175
2373
  type: 'TSQualifiedName',
2176
- left: getReactIdentifier(),
2374
+ left: getReactIdentifier(hasReactImport),
2177
2375
  right: {
2178
2376
  type: 'Identifier',
2179
2377
  name: `ComponentProps`,
@@ -2208,7 +2406,7 @@ const getTransforms = (
2208
2406
  type: 'TSTypeReference',
2209
2407
  typeName: {
2210
2408
  type: 'TSQualifiedName',
2211
- left: getReactIdentifier(),
2409
+ left: getReactIdentifier(hasReactImport),
2212
2410
  right: {
2213
2411
  type: 'Identifier',
2214
2412
  name: 'Ref',
@@ -2492,7 +2690,36 @@ const getTransforms = (
2492
2690
  ):
2493
2691
  | TSESTree.TSTypeLiteral
2494
2692
  | TSESTree.TSIntersectionType
2495
- | TSESTree.TSAnyKeyword {
2693
+ | TSESTree.TSAnyKeyword
2694
+ | TSESTree.TSMappedType {
2695
+ if (
2696
+ node.properties.length === 1 &&
2697
+ node.properties[0].type === 'ObjectTypeMappedTypeProperty'
2698
+ ) {
2699
+ // Mapped Object Object types must not have other object properties.
2700
+ const prop: FlowESTree.ObjectTypeMappedTypeProperty =
2701
+ node.properties[0];
2702
+ const tsProp: TSESTree.TSMappedType = {
2703
+ type: 'TSMappedType',
2704
+ typeParameter: {
2705
+ type: 'TSTypeParameter',
2706
+ name: {
2707
+ type: 'Identifier',
2708
+ name: prop.keyTparam.name,
2709
+ },
2710
+ constraint: transformTypeAnnotationType(prop.sourceType),
2711
+ in: false,
2712
+ out: false,
2713
+ },
2714
+ readonly: prop.variance?.kind === 'plus',
2715
+ optional: prop.optional === 'Optional',
2716
+ typeAnnotation: transformTypeAnnotationType(prop.propType),
2717
+ nameType: null,
2718
+ };
2719
+
2720
+ return tsProp;
2721
+ }
2722
+
2496
2723
  // we want to preserve the source order of the members
2497
2724
  // unfortunately flow has unordered properties storing things
2498
2725
  // so store all elements with their start index and sort the
@@ -2531,10 +2758,9 @@ const getTransforms = (
2531
2758
  }
2532
2759
 
2533
2760
  if (property.type === 'ObjectTypeMappedTypeProperty') {
2534
- // TODO - Support mapped types
2535
2761
  return unsupportedAnnotation(
2536
2762
  property,
2537
- 'object type with mapped type property',
2763
+ 'object type with mapped type property along with other properties',
2538
2764
  );
2539
2765
  }
2540
2766
 
@@ -3063,6 +3289,102 @@ const getTransforms = (
3063
3289
  type: 'TSVoidKeyword',
3064
3290
  };
3065
3291
  },
3292
+ ConditionalTypeAnnotation(
3293
+ node: FlowESTree.ConditionalTypeAnnotation,
3294
+ ): TSESTree.TSConditionalType {
3295
+ return {
3296
+ type: 'TSConditionalType',
3297
+ checkType: transformTypeAnnotationType(node.checkType),
3298
+ extendsType: transformTypeAnnotationType(node.extendsType),
3299
+ trueType: transformTypeAnnotationType(node.trueType),
3300
+ falseType: transformTypeAnnotationType(node.falseType),
3301
+ };
3302
+ },
3303
+ TypePredicateAnnotation(
3304
+ node: FlowESTree.TypePredicate,
3305
+ ): TSESTree.TSTypePredicate {
3306
+ return {
3307
+ type: 'TSTypePredicate',
3308
+ asserts: node.asserts,
3309
+ parameterName: transform.Identifier(node.parameterName, false),
3310
+ typeAnnotation: node.typeAnnotation && {
3311
+ type: 'TSTypeAnnotation',
3312
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation),
3313
+ },
3314
+ };
3315
+ },
3316
+ InferTypeAnnotation(
3317
+ node: FlowESTree.InferTypeAnnotation,
3318
+ ): TSESTree.TSInferType {
3319
+ return {
3320
+ type: 'TSInferType',
3321
+ typeParameter: transform.TypeParameter(node.typeParameter),
3322
+ };
3323
+ },
3324
+ KeyofTypeAnnotation(
3325
+ node: FlowESTree.KeyofTypeAnnotation,
3326
+ ): TSESTree.TSTypeOperator {
3327
+ return {
3328
+ type: 'TSTypeOperator',
3329
+ operator: 'keyof',
3330
+ typeAnnotation: transformTypeAnnotationType(node.argument),
3331
+ };
3332
+ },
3333
+ TypeOperator(node: FlowESTree.TypeOperator): TSESTree.TypeNode {
3334
+ switch (node.operator) {
3335
+ case 'renders': {
3336
+ const hasReactImport = isReactImport(node, 'React');
3337
+ return {
3338
+ type: 'TSTypeReference',
3339
+ typeName: {
3340
+ type: 'TSQualifiedName',
3341
+ left: getReactIdentifier(hasReactImport),
3342
+ right: {
3343
+ type: 'Identifier',
3344
+ name: `ReactNode`,
3345
+ },
3346
+ },
3347
+ typeParameters: undefined,
3348
+ };
3349
+ }
3350
+ }
3351
+ },
3352
+ ComponentTypeAnnotation(
3353
+ node: FlowESTree.ComponentTypeAnnotation,
3354
+ ): TSESTree.TSFunctionType {
3355
+ const typeParameters =
3356
+ node.typeParameters == null
3357
+ ? undefined
3358
+ : transform.TypeParameterDeclaration(node.typeParameters);
3359
+
3360
+ const params = transform.ComponentTypeParameters(node.params, node.rest);
3361
+
3362
+ // TS cannot support `renderType` so we always use ReactNode as the return type.
3363
+ const hasReactImport = isReactImport(node, 'React');
3364
+ const returnType = {
3365
+ type: 'TSTypeAnnotation',
3366
+ // If no rendersType we assume its ReactNode type.
3367
+ typeAnnotation: {
3368
+ type: 'TSTypeReference',
3369
+ typeName: {
3370
+ type: 'TSQualifiedName',
3371
+ left: getReactIdentifier(hasReactImport),
3372
+ right: {
3373
+ type: 'Identifier',
3374
+ name: `ReactNode`,
3375
+ },
3376
+ },
3377
+ typeParameters: undefined,
3378
+ },
3379
+ };
3380
+
3381
+ return {
3382
+ type: 'TSFunctionType',
3383
+ typeParameters,
3384
+ params,
3385
+ returnType,
3386
+ };
3387
+ },
3066
3388
  };
3067
3389
 
3068
3390
  // wrap each transform so that we automatically preserve jsdoc comments