flow-api-translator 0.10.0 → 0.11.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.
@@ -24,15 +24,25 @@ var _ErrorUtils = require("./utils/ErrorUtils");
24
24
 
25
25
  var _DocblockUtils = require("./utils/DocblockUtils");
26
26
 
27
+ var _os = require("os");
28
+
27
29
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
28
30
 
29
31
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
30
32
 
31
33
  const cloneJSDocCommentsToNewNode = // $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
32
34
  _hermesTransform.cloneJSDocCommentsToNewNode;
35
+ const makeCommentOwnLine = // $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
36
+ _hermesTransform.makeCommentOwnLine;
33
37
  const VALID_REACT_IMPORTS = new Set(['React', 'react']);
34
38
 
35
- function flowDefToTSDef(originalCode, ast, scopeManager) {
39
+ function isValidReactImportOrGlobal(id) {
40
+ return VALID_REACT_IMPORTS.has(id.name) || id.name.startsWith('React$');
41
+ }
42
+
43
+ let shouldAddReactImport = null;
44
+
45
+ function flowDefToTSDef(originalCode, ast, scopeManager, opts) {
36
46
  const tsBody = [];
37
47
  const tsProgram = {
38
48
  type: 'Program',
@@ -40,7 +50,8 @@ function flowDefToTSDef(originalCode, ast, scopeManager) {
40
50
  sourceType: ast.sourceType,
41
51
  docblock: ast.docblock == null ? null : (0, _DocblockUtils.removeAtFlowFromDocblock)(ast.docblock)
42
52
  };
43
- const transform = getTransforms(originalCode, scopeManager);
53
+ shouldAddReactImport = null;
54
+ const [transform, code] = getTransforms(originalCode, scopeManager, opts);
44
55
 
45
56
  for (const node of ast.body) {
46
57
  if (node.type in transform) {
@@ -51,15 +62,37 @@ function flowDefToTSDef(originalCode, ast, scopeManager) {
51
62
  tsBody.push(...(Array.isArray(result) ? result : [result]));
52
63
  } else {
53
64
  throw (0, _ErrorUtils.unexpectedTranslationError)(node, `Unexpected node type ${node.type}`, {
54
- code: originalCode
65
+ code
55
66
  });
56
67
  }
57
68
  }
58
69
 
59
- return tsProgram;
70
+ if (shouldAddReactImport === true) {
71
+ tsBody.unshift({
72
+ type: 'ImportDeclaration',
73
+ assertions: [],
74
+ source: {
75
+ type: 'Literal',
76
+ value: 'react',
77
+ raw: "'react'"
78
+ },
79
+ specifiers: [{
80
+ type: 'ImportNamespaceSpecifier',
81
+ local: {
82
+ type: 'Identifier',
83
+ name: 'React'
84
+ }
85
+ }],
86
+ importKind: 'value'
87
+ });
88
+ }
89
+
90
+ return [tsProgram, code];
60
91
  }
61
92
 
62
- const getTransforms = (code, scopeManager) => {
93
+ const getTransforms = (originalCode, scopeManager, opts) => {
94
+ let code = originalCode;
95
+
63
96
  function translationError(node, message) {
64
97
  return (0, _ErrorUtils.translationError)(node, message, {
65
98
  code
@@ -72,8 +105,66 @@ const getTransforms = (code, scopeManager) => {
72
105
  });
73
106
  }
74
107
 
75
- function unsupportedTranslationError(node, thing) {
76
- return translationError(node, `Unsupported feature: Translating "${thing}" is currently not supported.`);
108
+ function unsupportedFeatureMessage(thing) {
109
+ return `Unsupported feature: Translating "${thing}" is currently not supported.`;
110
+ }
111
+
112
+ function buildCodeFrameForComment(node, message) {
113
+ return (0, _ErrorUtils.buildCodeFrame)(node, message, code, false);
114
+ }
115
+
116
+ function addErrorComment(node, message) {
117
+ var _node$comments;
118
+
119
+ const comment = {
120
+ type: 'Block',
121
+ value: `*${_os.EOL} * ${message.replace(new RegExp(_os.EOL, 'g'), `${_os.EOL} * `)}${_os.EOL}*`,
122
+ leading: true,
123
+ printed: false
124
+ };
125
+ code = makeCommentOwnLine(code, comment); // $FlowExpectedError[prop-missing]
126
+ // $FlowExpectedError[cannot-write]
127
+
128
+ (_node$comments = node.comments) != null ? _node$comments : node.comments = []; // $FlowExpectedError[prop-missing]
129
+ // $FlowExpectedError[incompatible-cast]
130
+
131
+ node.comments.push(comment);
132
+ }
133
+
134
+ function unsupportedAnnotation(node, thing) {
135
+ const message = unsupportedFeatureMessage(thing);
136
+
137
+ if (opts.recoverFromErrors) {
138
+ const codeFrame = buildCodeFrameForComment(node, message);
139
+ const newNode = {
140
+ type: 'TSAnyKeyword'
141
+ };
142
+ addErrorComment(newNode, codeFrame);
143
+ return newNode;
144
+ }
145
+
146
+ throw translationError(node, message);
147
+ }
148
+
149
+ function unsupportedDeclaration(node, thing, id, declare = false, typeParameters = null) {
150
+ const message = unsupportedFeatureMessage(thing);
151
+
152
+ if (opts.recoverFromErrors) {
153
+ const codeFrame = buildCodeFrameForComment(node, message);
154
+ const newNode = {
155
+ type: 'TSTypeAliasDeclaration',
156
+ declare,
157
+ id: transform.Identifier(id, false),
158
+ typeAnnotation: {
159
+ type: 'TSAnyKeyword'
160
+ },
161
+ typeParameters: typeParameters == null ? undefined : transform.TypeParameterDeclaration(typeParameters)
162
+ };
163
+ addErrorComment(newNode, codeFrame);
164
+ return newNode;
165
+ }
166
+
167
+ throw translationError(node, message);
77
168
  }
78
169
 
79
170
  const topScope = (() => {
@@ -87,7 +178,21 @@ const getTransforms = (code, scopeManager) => {
87
178
  })();
88
179
 
89
180
  function isReactImport(id) {
90
- let currentScope = scopeManager.acquire(id);
181
+ let currentScope = (() => {
182
+ let scope = null;
183
+ let node = id;
184
+
185
+ while (!scope && node) {
186
+ scope = scopeManager.acquire(node, true);
187
+ node = node.parent;
188
+ }
189
+
190
+ return scope;
191
+ })();
192
+
193
+ if (currentScope == null) {
194
+ throw new Error('unable to resolve scope');
195
+ }
91
196
 
92
197
  const variableDef = (() => {
93
198
  while (currentScope != null) {
@@ -99,11 +204,12 @@ const getTransforms = (code, scopeManager) => {
99
204
 
100
205
  currentScope = currentScope.upper;
101
206
  }
102
- })(); // No variable found, it must be global. Using the `React` variable is enough in this case.
207
+ })(); // No variable found, it is not imported.
208
+ // It could be a global though if isValidReactImportOrGlobal returns true.
103
209
 
104
210
 
105
211
  if (variableDef == null) {
106
- return VALID_REACT_IMPORTS.has(id.name);
212
+ return false;
107
213
  }
108
214
 
109
215
  const def = variableDef.defs[0]; // Detect:
@@ -133,6 +239,393 @@ const getTransforms = (code, scopeManager) => {
133
239
  return false;
134
240
  }
135
241
 
242
+ function EnumImpl(node) {
243
+ const body = node.body;
244
+
245
+ if (body.type === 'EnumSymbolBody') {
246
+ /*
247
+ There's unfortunately no way for us to support this in a clean way.
248
+ We can get really close using this code:
249
+ ```
250
+ declare namespace SymbolEnum {
251
+ export const member1: unique symbol;
252
+ export type member1 = typeof member1;
253
+ export const member2: unique symbol;
254
+ export type member2 = typeof member2;
255
+ }
256
+ type SymbolEnum = typeof SymbolEnum[keyof typeof SymbolEnum];
257
+ ```
258
+ However as explained in https://github.com/microsoft/TypeScript/issues/43657:
259
+ "A unique symbol type is never transferred from one declaration to another through inference."
260
+ This intended behaviour in TS means that the usage of the fake-enum would look like this:
261
+ ```
262
+ const value: SymbolEnum.member1 = SymbolEnum.member1;
263
+ // ^^^^^^^^^^^^^^^^^^ required to force TS to retain the information
264
+ ```
265
+ Which is really clunky and shitty. It definitely works, but ofc it's not good.
266
+ We can go with this design if users are okay with it!
267
+ Considering how rarely used symbol enums are ATM, let's just put a pin in it for now.
268
+ */
269
+ return unsupportedDeclaration(node, 'symbol enums', node.id, FlowESTree.isDeclareEnum(node));
270
+ }
271
+
272
+ if (body.type === 'EnumBooleanBody') {
273
+ /*
274
+ TODO - TS enums only allow strings or numbers as their values - not booleans.
275
+ This means we need a non-ts-enum representation of the enum.
276
+ We can support boolean enums using a construct like this:
277
+ ```ts
278
+ declare namespace BooleanEnum {
279
+ export const member1: true;
280
+ export type member1 = typeof member1;
281
+ export const member2: false;
282
+ export type member2 = typeof member1;
283
+ }
284
+ declare type BooleanEnum = boolean;
285
+ ```
286
+ But it's pretty clunky and ugly.
287
+ Considering how rarely used boolean enums are ATM, let's just put a pin in it for now.
288
+ */
289
+ return unsupportedDeclaration(node, 'boolean enums', node.id, FlowESTree.isDeclareEnum(node));
290
+ }
291
+
292
+ const members = [];
293
+
294
+ for (const member of body.members) {
295
+ switch (member.type) {
296
+ case 'EnumDefaultedMember':
297
+ {
298
+ if (body.type === 'EnumNumberBody') {
299
+ // this should be impossible!
300
+ throw unexpectedTranslationError(member, 'Unexpected defaulted number enum member');
301
+ }
302
+
303
+ members.push({
304
+ type: 'TSEnumMember',
305
+ computed: false,
306
+ id: transform.Identifier(member.id, false),
307
+ initializer: {
308
+ type: 'Literal',
309
+ raw: `"${member.id.name}"`,
310
+ value: member.id.name
311
+ }
312
+ });
313
+ break;
314
+ }
315
+
316
+ case 'EnumNumberMember':
317
+ case 'EnumStringMember':
318
+ members.push({
319
+ type: 'TSEnumMember',
320
+ computed: false,
321
+ id: transform.Identifier(member.id, false),
322
+ initializer: member.init.literalType === 'string' ? transform.StringLiteral(member.init) : transform.NumericLiteral(member.init)
323
+ });
324
+ }
325
+ }
326
+
327
+ const bodyRepresentationType = body.type === 'EnumNumberBody' ? {
328
+ type: 'TSNumberKeyword'
329
+ } : {
330
+ type: 'TSStringKeyword'
331
+ };
332
+ const enumName = transform.Identifier(node.id, false);
333
+ return [{
334
+ type: 'TSEnumDeclaration',
335
+ const: false,
336
+ declare: true,
337
+ id: enumName,
338
+ members
339
+ }, // flow also exports `.cast`, `.isValid`, `.members` and `.getName` for enums
340
+ // we can use declaration merging to declare these functions on the enum:
341
+
342
+ /*
343
+ declare enum Foo {
344
+ A = 1,
345
+ B = 2,
346
+ }
347
+ declare namespace Foo {
348
+ export function cast(value: number | null | undefined): Foo;
349
+ export function isValid(value: number | null | undefined): value is Foo;
350
+ export function members(): IterableIterator<Foo>;
351
+ export function getName(value: Foo): string;
352
+ }
353
+ */
354
+ {
355
+ type: 'TSModuleDeclaration',
356
+ declare: true,
357
+ id: enumName,
358
+ body: {
359
+ type: 'TSModuleBlock',
360
+ body: [// export function cast(value: number | null | undefined): Foo
361
+ {
362
+ type: 'ExportNamedDeclaration',
363
+ declaration: {
364
+ type: 'TSDeclareFunction',
365
+ id: {
366
+ type: 'Identifier',
367
+ name: 'cast'
368
+ },
369
+ generator: false,
370
+ expression: false,
371
+ async: false,
372
+ params: [{
373
+ type: 'Identifier',
374
+ name: 'value',
375
+ typeAnnotation: {
376
+ type: 'TSTypeAnnotation',
377
+ typeAnnotation: {
378
+ type: 'TSUnionType',
379
+ types: [bodyRepresentationType, {
380
+ type: 'TSNullKeyword'
381
+ }, {
382
+ type: 'TSUndefinedKeyword'
383
+ }]
384
+ }
385
+ }
386
+ }],
387
+ returnType: {
388
+ type: 'TSTypeAnnotation',
389
+ typeAnnotation: {
390
+ type: 'TSTypeReference',
391
+ typeName: enumName
392
+ }
393
+ }
394
+ },
395
+ specifiers: [],
396
+ source: null,
397
+ exportKind: 'value',
398
+ assertions: []
399
+ }, // export function isValid(value: number | null | undefined): value is Foo;
400
+ {
401
+ type: 'ExportNamedDeclaration',
402
+ declaration: {
403
+ type: 'TSDeclareFunction',
404
+ id: {
405
+ type: 'Identifier',
406
+ name: 'isValid'
407
+ },
408
+ generator: false,
409
+ expression: false,
410
+ async: false,
411
+ params: [{
412
+ type: 'Identifier',
413
+ name: 'value',
414
+ typeAnnotation: {
415
+ type: 'TSTypeAnnotation',
416
+ typeAnnotation: {
417
+ type: 'TSUnionType',
418
+ types: [bodyRepresentationType, {
419
+ type: 'TSNullKeyword'
420
+ }, {
421
+ type: 'TSUndefinedKeyword'
422
+ }]
423
+ }
424
+ }
425
+ }],
426
+ returnType: {
427
+ type: 'TSTypeAnnotation',
428
+ typeAnnotation: {
429
+ type: 'TSTypePredicate',
430
+ asserts: false,
431
+ parameterName: {
432
+ type: 'Identifier',
433
+ name: 'value'
434
+ },
435
+ typeAnnotation: {
436
+ type: 'TSTypeAnnotation',
437
+ typeAnnotation: {
438
+ type: 'TSTypeReference',
439
+ typeName: enumName
440
+ }
441
+ }
442
+ }
443
+ }
444
+ },
445
+ specifiers: [],
446
+ source: null,
447
+ exportKind: 'value',
448
+ assertions: []
449
+ }, // export function members(): IterableIterator<Foo>;
450
+ {
451
+ type: 'ExportNamedDeclaration',
452
+ declaration: {
453
+ type: 'TSDeclareFunction',
454
+ id: {
455
+ type: 'Identifier',
456
+ name: 'members'
457
+ },
458
+ generator: false,
459
+ expression: false,
460
+ async: false,
461
+ params: [],
462
+ returnType: {
463
+ type: 'TSTypeAnnotation',
464
+ typeAnnotation: {
465
+ type: 'TSTypeReference',
466
+ typeName: {
467
+ type: 'Identifier',
468
+ name: 'IterableIterator'
469
+ },
470
+ typeParameters: {
471
+ type: 'TSTypeParameterInstantiation',
472
+ params: [{
473
+ type: 'TSTypeReference',
474
+ typeName: enumName
475
+ }]
476
+ }
477
+ }
478
+ }
479
+ },
480
+ specifiers: [],
481
+ source: null,
482
+ exportKind: 'value',
483
+ assertions: []
484
+ }, // export function getName(value: Foo): string;
485
+ {
486
+ type: 'ExportNamedDeclaration',
487
+ declaration: {
488
+ type: 'TSDeclareFunction',
489
+ id: {
490
+ type: 'Identifier',
491
+ name: 'getName'
492
+ },
493
+ generator: false,
494
+ expression: false,
495
+ async: false,
496
+ params: [{
497
+ type: 'Identifier',
498
+ name: 'value',
499
+ typeAnnotation: {
500
+ type: 'TSTypeAnnotation',
501
+ typeAnnotation: {
502
+ type: 'TSTypeReference',
503
+ typeName: enumName
504
+ }
505
+ }
506
+ }],
507
+ returnType: {
508
+ type: 'TSTypeAnnotation',
509
+ typeAnnotation: {
510
+ type: 'TSStringKeyword'
511
+ }
512
+ }
513
+ },
514
+ specifiers: [],
515
+ source: null,
516
+ exportKind: 'value',
517
+ assertions: []
518
+ }]
519
+ }
520
+ }];
521
+ }
522
+
523
+ const getPlaceholderNameForTypeofImport = (() => {
524
+ let typeof_import_count = 0;
525
+ return () => `$$IMPORT_TYPEOF_${++typeof_import_count}$$`;
526
+ })();
527
+
528
+ const transformTypeAnnotationType = node => {
529
+ switch (node.type) {
530
+ case 'AnyTypeAnnotation':
531
+ return transform.AnyTypeAnnotation(node);
532
+
533
+ case 'ArrayTypeAnnotation':
534
+ return transform.ArrayTypeAnnotation(node);
535
+
536
+ case 'BigIntLiteralTypeAnnotation':
537
+ return transform.BigIntLiteralTypeAnnotation(node);
538
+
539
+ case 'BigIntTypeAnnotation':
540
+ return transform.BigIntTypeAnnotation(node);
541
+
542
+ case 'BooleanLiteralTypeAnnotation':
543
+ return transform.BooleanLiteralTypeAnnotation(node);
544
+
545
+ case 'BooleanTypeAnnotation':
546
+ return transform.BooleanTypeAnnotation(node);
547
+
548
+ case 'EmptyTypeAnnotation':
549
+ return transform.EmptyTypeAnnotation(node);
550
+
551
+ case 'ExistsTypeAnnotation':
552
+ return transform.ExistsTypeAnnotation(node);
553
+
554
+ case 'FunctionTypeAnnotation':
555
+ return transform.FunctionTypeAnnotation(node);
556
+
557
+ case 'GenericTypeAnnotation':
558
+ return transform.GenericTypeAnnotation(node);
559
+
560
+ case 'IndexedAccessType':
561
+ return transform.IndexedAccessType(node);
562
+
563
+ case 'InterfaceTypeAnnotation':
564
+ return transform.InterfaceTypeAnnotation(node);
565
+
566
+ case 'IntersectionTypeAnnotation':
567
+ return transform.IntersectionTypeAnnotation(node);
568
+
569
+ case 'MixedTypeAnnotation':
570
+ return transform.MixedTypeAnnotation(node);
571
+
572
+ case 'NullLiteralTypeAnnotation':
573
+ return transform.NullLiteralTypeAnnotation(node);
574
+
575
+ case 'NullableTypeAnnotation':
576
+ return transform.NullableTypeAnnotation(node);
577
+
578
+ case 'NumberLiteralTypeAnnotation':
579
+ return transform.NumberLiteralTypeAnnotation(node);
580
+
581
+ case 'NumberTypeAnnotation':
582
+ return transform.NumberTypeAnnotation(node);
583
+
584
+ case 'ObjectTypeAnnotation':
585
+ return transform.ObjectTypeAnnotation(node);
586
+
587
+ case 'OptionalIndexedAccessType':
588
+ return transform.OptionalIndexedAccessType(node);
589
+
590
+ case 'QualifiedTypeIdentifier':
591
+ return transform.QualifiedTypeIdentifier(node);
592
+
593
+ case 'StringLiteralTypeAnnotation':
594
+ return transform.StringLiteralTypeAnnotation(node);
595
+
596
+ case 'StringTypeAnnotation':
597
+ return transform.StringTypeAnnotation(node);
598
+
599
+ case 'SymbolTypeAnnotation':
600
+ return transform.SymbolTypeAnnotation(node);
601
+
602
+ case 'ThisTypeAnnotation':
603
+ return transform.ThisTypeAnnotation(node);
604
+
605
+ case 'TupleTypeAnnotation':
606
+ return transform.TupleTypeAnnotation(node);
607
+
608
+ case 'TupleTypeLabeledElement':
609
+ case 'TupleTypeSpreadElement':
610
+ return unsupportedAnnotation(node, node.type);
611
+
612
+ case 'TypeofTypeAnnotation':
613
+ return transform.TypeofTypeAnnotation(node);
614
+
615
+ case 'UnionTypeAnnotation':
616
+ return transform.UnionTypeAnnotation(node);
617
+
618
+ case 'VoidTypeAnnotation':
619
+ return transform.VoidTypeAnnotation(node);
620
+
621
+ case 'TypePredicate':
622
+ return unsupportedAnnotation(node, node.type);
623
+
624
+ default:
625
+ throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
626
+ }
627
+ };
628
+
136
629
  const transform = {
137
630
  AnyTypeAnnotation(_node) {
138
631
  return {
@@ -143,7 +636,7 @@ const getTransforms = (code, scopeManager) => {
143
636
  ArrayTypeAnnotation(node) {
144
637
  return {
145
638
  type: 'TSArrayType',
146
- elementType: transform.TypeAnnotationType(node.elementType)
639
+ elementType: transformTypeAnnotationType(node.elementType)
147
640
  };
148
641
  },
149
642
 
@@ -221,7 +714,7 @@ const getTransforms = (code, scopeManager) => {
221
714
  const transformedBody = transform.ObjectTypeAnnotation(node.body);
222
715
 
223
716
  if (transformedBody.type !== 'TSTypeLiteral') {
224
- throw translationError(node.body, 'Spreads in declare class are not allowed');
717
+ return unsupportedDeclaration(node.body, 'Spreads in declare class are not allowed', node.id, true, node.typeParameters);
225
718
  }
226
719
 
227
720
  for (const member of transformedBody.members) {
@@ -348,7 +841,7 @@ const getTransforms = (code, scopeManager) => {
348
841
  ```
349
842
  Let's put a pin in it for now and deal with it later if the need arises.
350
843
  */
351
- throw unsupportedTranslationError((_node$body$callProper = node.body.callProperties[0]) != null ? _node$body$callProper : node.body, 'call signatures on classes');
844
+ return unsupportedDeclaration((_node$body$callProper = node.body.callProperties[0]) != null ? _node$body$callProper : node.body, 'call signatures on classes', node.id, true, node.typeParameters);
352
845
  }
353
846
 
354
847
  default:
@@ -421,50 +914,48 @@ const getTransforms = (code, scopeManager) => {
421
914
  if (referencedId.type === 'Identifier') {
422
915
  const exportedVar = topScope.set.get(referencedId.name);
423
916
 
424
- if (exportedVar == null || exportedVar.defs.length !== 1) {
425
- throw unexpectedTranslationError(referencedId, `Unable to find exported variable ${referencedId.name}`);
426
- }
427
-
428
- const def = exportedVar.defs[0];
429
-
430
- switch (def.type) {
431
- case 'ImportBinding':
432
- {
433
- // `import type { Wut } from 'mod'; declare export default Wut;`
434
- // `import { type Wut } from 'mod'; declare export default Wut;`
435
- // these cases should be wrapped in a variable because they're exporting a type, not a value
436
- const specifier = def.node;
917
+ if (exportedVar != null && exportedVar.defs.length === 1) {
918
+ const def = exportedVar.defs[0];
437
919
 
438
- if (specifier.importKind === 'type' || specifier.parent.importKind === 'type') {
439
- // fallthrough to the "default" handling
440
- break;
441
- } // intentional fallthrough to the "value" handling
920
+ switch (def.type) {
921
+ case 'ImportBinding':
922
+ {
923
+ // `import type { Wut } from 'mod'; declare export default Wut;`
924
+ // `import { type Wut } from 'mod'; declare export default Wut;`
925
+ // these cases should be wrapped in a variable because they're exporting a type, not a value
926
+ const specifier = def.node;
442
927
 
443
- }
444
-
445
- case 'ClassName':
446
- case 'Enum':
447
- case 'FunctionName':
448
- case 'ImplicitGlobalVariable':
449
- case 'Variable':
450
- // there's already a variable defined to hold the type
451
- return {
452
- type: 'ExportDefaultDeclaration',
453
- declaration: {
454
- type: 'Identifier',
455
- name: referencedId.name
456
- },
457
- exportKind: 'value'
458
- };
928
+ if (specifier.importKind === 'type' || specifier.parent.importKind === 'type') {
929
+ // fallthrough to the "default" handling
930
+ break;
931
+ } // intentional fallthrough to the "value" handling
459
932
 
460
- case 'CatchClause':
461
- case 'Parameter':
462
- case 'TypeParameter':
463
- throw translationError(def.node, `Unexpected variable def type: ${def.type}`);
933
+ }
464
934
 
465
- case 'Type':
466
- // fallthrough to the "default" handling
467
- break;
935
+ case 'ClassName':
936
+ case 'Enum':
937
+ case 'FunctionName':
938
+ case 'ImplicitGlobalVariable':
939
+ case 'Variable':
940
+ // there's already a variable defined to hold the type
941
+ return {
942
+ type: 'ExportDefaultDeclaration',
943
+ declaration: {
944
+ type: 'Identifier',
945
+ name: referencedId.name
946
+ },
947
+ exportKind: 'value'
948
+ };
949
+
950
+ case 'CatchClause':
951
+ case 'Parameter':
952
+ case 'TypeParameter':
953
+ throw translationError(def.node, `Unexpected variable def type: ${def.type}`);
954
+
955
+ case 'Type':
956
+ // fallthrough to the "default" handling
957
+ break;
958
+ }
468
959
  }
469
960
  } // intentional fallthrough to the "default" handling
470
961
 
@@ -494,7 +985,7 @@ const getTransforms = (code, scopeManager) => {
494
985
  name: SPECIFIER,
495
986
  typeAnnotation: {
496
987
  type: 'TSTypeAnnotation',
497
- typeAnnotation: transform.TypeAnnotationType(declaration)
988
+ typeAnnotation: transformTypeAnnotationType(declaration)
498
989
  }
499
990
  },
500
991
  init: null
@@ -528,44 +1019,59 @@ const getTransforms = (code, scopeManager) => {
528
1019
  };
529
1020
  }
530
1021
 
531
- const {
532
- declaration,
533
- exportKind
534
- } = (() => {
1022
+ const declarations = (() => {
535
1023
  switch (node.declaration.type) {
536
1024
  case 'DeclareClass':
537
- return {
1025
+ return [{
538
1026
  declaration: transform.DeclareClass(node.declaration),
539
1027
  exportKind: 'value'
540
- };
1028
+ }];
541
1029
 
542
1030
  case 'DeclareFunction':
543
- return {
1031
+ return [{
544
1032
  declaration: transform.DeclareFunction(node.declaration),
545
1033
  exportKind: 'value'
546
- };
1034
+ }];
547
1035
 
548
1036
  case 'DeclareInterface':
549
- return {
1037
+ return [{
550
1038
  declaration: transform.DeclareInterface(node.declaration),
551
1039
  exportKind: 'type'
552
- };
1040
+ }];
553
1041
 
554
1042
  case 'DeclareOpaqueType':
555
- return {
1043
+ return [{
556
1044
  declaration: transform.DeclareOpaqueType(node.declaration),
557
1045
  exportKind: 'type'
558
- };
1046
+ }];
559
1047
 
560
1048
  case 'DeclareVariable':
561
- return {
1049
+ return [{
562
1050
  declaration: transform.DeclareVariable(node.declaration),
563
1051
  exportKind: 'value'
564
- };
1052
+ }];
1053
+
1054
+ case 'DeclareEnum':
1055
+ {
1056
+ const result = transform.DeclareEnum(node.declaration);
1057
+ return Array.isArray(result) ? [{
1058
+ declaration: result[0],
1059
+ exportKind: 'type'
1060
+ }, {
1061
+ declaration: result[1],
1062
+ exportKind: 'type'
1063
+ }] : [{
1064
+ declaration: result,
1065
+ exportKind: 'type'
1066
+ }];
1067
+ }
565
1068
  }
566
1069
  })();
567
1070
 
568
- return {
1071
+ return declarations.map(({
1072
+ declaration,
1073
+ exportKind
1074
+ }) => ({
569
1075
  type: 'ExportNamedDeclaration',
570
1076
  // flow does not currently support assertions
571
1077
  assertions: [],
@@ -573,7 +1079,7 @@ const getTransforms = (code, scopeManager) => {
573
1079
  exportKind,
574
1080
  source: null,
575
1081
  specifiers: []
576
- };
1082
+ }));
577
1083
  } else {
578
1084
  return {
579
1085
  type: 'ExportNamedDeclaration',
@@ -635,7 +1141,7 @@ const getTransforms = (code, scopeManager) => {
635
1141
  type: 'TSTypeAliasDeclaration',
636
1142
  declare: node.type === 'DeclareTypeAlias',
637
1143
  id: transform.Identifier(node.id, false),
638
- typeAnnotation: transform.TypeAnnotationType(node.right),
1144
+ typeAnnotation: transformTypeAnnotationType(node.right),
639
1145
  typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
640
1146
  };
641
1147
  },
@@ -650,7 +1156,7 @@ const getTransforms = (code, scopeManager) => {
650
1156
  id: transform.Identifier(node.id, false),
651
1157
  typeAnnotation: node.supertype == null ? {
652
1158
  type: 'TSUnknownKeyword'
653
- } : transform.TypeAnnotationType(node.supertype),
1159
+ } : transformTypeAnnotationType(node.supertype),
654
1160
  typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
655
1161
  };
656
1162
  },
@@ -665,297 +1171,24 @@ const getTransforms = (code, scopeManager) => {
665
1171
  id: transform.Identifier(node.id, true),
666
1172
  init: null
667
1173
  }],
668
- kind: 'var'
1174
+ kind: node.kind
669
1175
  };
670
1176
  },
671
1177
 
1178
+ DeclareEnum(node) {
1179
+ return EnumImpl(node);
1180
+ },
1181
+
672
1182
  EmptyTypeAnnotation(node) {
673
1183
  // Flow's `empty` type doesn't map well to any types in TS.
674
1184
  // The closest is `never`, but `never` has a number of different semantics
675
1185
  // In reality no human code should ever directly use the `empty` type in flow
676
1186
  // So let's put a pin in it for now
677
- throw unsupportedTranslationError(node, 'empty type');
1187
+ return unsupportedAnnotation(node, 'empty type');
678
1188
  },
679
1189
 
680
1190
  EnumDeclaration(node) {
681
- const body = node.body;
682
-
683
- if (body.type === 'EnumSymbolBody') {
684
- /*
685
- There's unfortunately no way for us to support this in a clean way.
686
- We can get really close using this code:
687
- ```
688
- declare namespace SymbolEnum {
689
- export const member1: unique symbol;
690
- export type member1 = typeof member1;
691
- export const member2: unique symbol;
692
- export type member2 = typeof member2;
693
- }
694
- type SymbolEnum = typeof SymbolEnum[keyof typeof SymbolEnum];
695
- ```
696
- However as explained in https://github.com/microsoft/TypeScript/issues/43657:
697
- "A unique symbol type is never transferred from one declaration to another through inference."
698
- This intended behaviour in TS means that the usage of the fake-enum would look like this:
699
- ```
700
- const value: SymbolEnum.member1 = SymbolEnum.member1;
701
- // ^^^^^^^^^^^^^^^^^^ required to force TS to retain the information
702
- ```
703
- Which is really clunky and shitty. It definitely works, but ofc it's not good.
704
- We can go with this design if users are okay with it!
705
- Considering how rarely used symbol enums are ATM, let's just put a pin in it for now.
706
- */
707
- throw unsupportedTranslationError(node, 'symbol enums');
708
- }
709
-
710
- if (body.type === 'EnumBooleanBody') {
711
- /*
712
- TODO - TS enums only allow strings or numbers as their values - not booleans.
713
- This means we need a non-ts-enum representation of the enum.
714
- We can support boolean enums using a construct like this:
715
- ```ts
716
- declare namespace BooleanEnum {
717
- export const member1: true;
718
- export type member1 = typeof member1;
719
- export const member2: false;
720
- export type member2 = typeof member1;
721
- }
722
- declare type BooleanEnum = boolean;
723
- ```
724
- But it's pretty clunky and ugly.
725
- Considering how rarely used boolean enums are ATM, let's just put a pin in it for now.
726
- */
727
- throw unsupportedTranslationError(node, 'boolean enums');
728
- }
729
-
730
- const members = [];
731
-
732
- for (const member of body.members) {
733
- switch (member.type) {
734
- case 'EnumDefaultedMember':
735
- {
736
- if (body.type === 'EnumNumberBody') {
737
- // this should be impossible!
738
- throw unexpectedTranslationError(member, 'Unexpected defaulted number enum member');
739
- }
740
-
741
- members.push({
742
- type: 'TSEnumMember',
743
- computed: false,
744
- id: transform.Identifier(member.id, false),
745
- initializer: {
746
- type: 'Literal',
747
- raw: `"${member.id.name}"`,
748
- value: member.id.name
749
- }
750
- });
751
- break;
752
- }
753
-
754
- case 'EnumNumberMember':
755
- case 'EnumStringMember':
756
- members.push({
757
- type: 'TSEnumMember',
758
- computed: false,
759
- id: transform.Identifier(member.id, false),
760
- initializer: member.init.literalType === 'string' ? transform.StringLiteral(member.init) : transform.NumericLiteral(member.init)
761
- });
762
- }
763
- }
764
-
765
- const bodyRepresentationType = body.type === 'EnumNumberBody' ? {
766
- type: 'TSNumberKeyword'
767
- } : {
768
- type: 'TSStringKeyword'
769
- };
770
- const enumName = transform.Identifier(node.id, false);
771
- return [{
772
- type: 'TSEnumDeclaration',
773
- const: false,
774
- declare: true,
775
- id: enumName,
776
- members
777
- }, // flow also exports `.cast`, `.isValid`, `.members` and `.getName` for enums
778
- // we can use declaration merging to declare these functions on the enum:
779
-
780
- /*
781
- declare enum Foo {
782
- A = 1,
783
- B = 2,
784
- }
785
- declare namespace Foo {
786
- export function cast(value: number | null | undefined): Foo;
787
- export function isValid(value: number | null | undefined): value is Foo;
788
- export function members(): IterableIterator<Foo>;
789
- export function getName(value: Foo): string;
790
- }
791
- */
792
- {
793
- type: 'TSModuleDeclaration',
794
- declare: true,
795
- id: enumName,
796
- body: {
797
- type: 'TSModuleBlock',
798
- body: [// export function cast(value: number | null | undefined): Foo
799
- {
800
- type: 'ExportNamedDeclaration',
801
- declaration: {
802
- type: 'TSDeclareFunction',
803
- id: {
804
- type: 'Identifier',
805
- name: 'cast'
806
- },
807
- generator: false,
808
- expression: false,
809
- async: false,
810
- params: [{
811
- type: 'Identifier',
812
- name: 'value',
813
- typeAnnotation: {
814
- type: 'TSTypeAnnotation',
815
- typeAnnotation: {
816
- type: 'TSUnionType',
817
- types: [bodyRepresentationType, {
818
- type: 'TSNullKeyword'
819
- }, {
820
- type: 'TSUndefinedKeyword'
821
- }]
822
- }
823
- }
824
- }],
825
- returnType: {
826
- type: 'TSTypeAnnotation',
827
- typeAnnotation: {
828
- type: 'TSTypeReference',
829
- typeName: enumName
830
- }
831
- }
832
- },
833
- specifiers: [],
834
- source: null,
835
- exportKind: 'value',
836
- assertions: []
837
- }, // export function isValid(value: number | null | undefined): value is Foo;
838
- {
839
- type: 'ExportNamedDeclaration',
840
- declaration: {
841
- type: 'TSDeclareFunction',
842
- id: {
843
- type: 'Identifier',
844
- name: 'isValid'
845
- },
846
- generator: false,
847
- expression: false,
848
- async: false,
849
- params: [{
850
- type: 'Identifier',
851
- name: 'value',
852
- typeAnnotation: {
853
- type: 'TSTypeAnnotation',
854
- typeAnnotation: {
855
- type: 'TSUnionType',
856
- types: [bodyRepresentationType, {
857
- type: 'TSNullKeyword'
858
- }, {
859
- type: 'TSUndefinedKeyword'
860
- }]
861
- }
862
- }
863
- }],
864
- returnType: {
865
- type: 'TSTypeAnnotation',
866
- typeAnnotation: {
867
- type: 'TSTypePredicate',
868
- asserts: false,
869
- parameterName: {
870
- type: 'Identifier',
871
- name: 'value'
872
- },
873
- typeAnnotation: {
874
- type: 'TSTypeAnnotation',
875
- typeAnnotation: {
876
- type: 'TSTypeReference',
877
- typeName: enumName
878
- }
879
- }
880
- }
881
- }
882
- },
883
- specifiers: [],
884
- source: null,
885
- exportKind: 'value',
886
- assertions: []
887
- }, // export function members(): IterableIterator<Foo>;
888
- {
889
- type: 'ExportNamedDeclaration',
890
- declaration: {
891
- type: 'TSDeclareFunction',
892
- id: {
893
- type: 'Identifier',
894
- name: 'members'
895
- },
896
- generator: false,
897
- expression: false,
898
- async: false,
899
- params: [],
900
- returnType: {
901
- type: 'TSTypeAnnotation',
902
- typeAnnotation: {
903
- type: 'TSTypeReference',
904
- typeName: {
905
- type: 'Identifier',
906
- name: 'IterableIterator'
907
- },
908
- typeParameters: {
909
- type: 'TSTypeParameterInstantiation',
910
- params: [{
911
- type: 'TSTypeReference',
912
- typeName: enumName
913
- }]
914
- }
915
- }
916
- }
917
- },
918
- specifiers: [],
919
- source: null,
920
- exportKind: 'value',
921
- assertions: []
922
- }, // export function getName(value: Foo): string;
923
- {
924
- type: 'ExportNamedDeclaration',
925
- declaration: {
926
- type: 'TSDeclareFunction',
927
- id: {
928
- type: 'Identifier',
929
- name: 'getName'
930
- },
931
- generator: false,
932
- expression: false,
933
- async: false,
934
- params: [{
935
- type: 'Identifier',
936
- name: 'value',
937
- typeAnnotation: {
938
- type: 'TSTypeAnnotation',
939
- typeAnnotation: {
940
- type: 'TSTypeReference',
941
- typeName: enumName
942
- }
943
- }
944
- }],
945
- returnType: {
946
- type: 'TSTypeAnnotation',
947
- typeAnnotation: {
948
- type: 'TSStringKeyword'
949
- }
950
- }
951
- },
952
- specifiers: [],
953
- source: null,
954
- exportKind: 'value',
955
- assertions: []
956
- }]
957
- }
958
- }];
1191
+ return EnumImpl(node);
959
1192
  },
960
1193
 
961
1194
  DeclareModuleExports(node) {
@@ -965,7 +1198,7 @@ const getTransforms = (code, scopeManager) => {
965
1198
  ExistsTypeAnnotation(node) {
966
1199
  // The existential type does not map to any types in TS
967
1200
  // It's also super deprecated - so let's not ever worry
968
- throw unsupportedTranslationError(node, 'exestential type');
1201
+ return unsupportedAnnotation(node, 'existential type');
969
1202
  },
970
1203
 
971
1204
  ExportNamedDeclaration(node) {
@@ -984,11 +1217,12 @@ const getTransforms = (code, scopeManager) => {
984
1217
 
985
1218
  const [exportedDeclaration, mergedDeclaration] = (() => {
986
1219
  if (node.declaration == null) {
987
- return [null];
1220
+ return [null, null];
988
1221
  }
989
1222
 
990
1223
  switch (node.declaration.type) {
991
1224
  case 'ClassDeclaration':
1225
+ case 'ComponentDeclaration':
992
1226
  case 'FunctionDeclaration':
993
1227
  case 'VariableDeclaration':
994
1228
  // These cases shouldn't happen in flow defs because they have their own special
@@ -996,7 +1230,10 @@ const getTransforms = (code, scopeManager) => {
996
1230
  throw unexpectedTranslationError(node.declaration, `Unexpected named declaration found ${node.declaration.type}`);
997
1231
 
998
1232
  case 'EnumDeclaration':
999
- return transform.EnumDeclaration(node.declaration);
1233
+ {
1234
+ const result = transform.EnumDeclaration(node.declaration);
1235
+ return Array.isArray(result) ? result : [result, null];
1236
+ }
1000
1237
 
1001
1238
  case 'InterfaceDeclaration':
1002
1239
  return [transform.InterfaceDeclaration(node.declaration), null];
@@ -1052,7 +1289,7 @@ const getTransforms = (code, scopeManager) => {
1052
1289
  name: 'this',
1053
1290
  typeAnnotation: {
1054
1291
  type: 'TSTypeAnnotation',
1055
- typeAnnotation: transform.TypeAnnotationType(node.this.typeAnnotation)
1292
+ typeAnnotation: transformTypeAnnotationType(node.this.typeAnnotation)
1056
1293
  }
1057
1294
  });
1058
1295
  }
@@ -1067,7 +1304,7 @@ const getTransforms = (code, scopeManager) => {
1067
1304
  } : transform.Identifier(rest.name, false),
1068
1305
  typeAnnotation: {
1069
1306
  type: 'TSTypeAnnotation',
1070
- typeAnnotation: transform.TypeAnnotationType(rest.typeAnnotation)
1307
+ typeAnnotation: transformTypeAnnotationType(rest.typeAnnotation)
1071
1308
  }
1072
1309
  });
1073
1310
  }
@@ -1077,7 +1314,7 @@ const getTransforms = (code, scopeManager) => {
1077
1314
  params,
1078
1315
  returnType: {
1079
1316
  type: 'TSTypeAnnotation',
1080
- typeAnnotation: transform.TypeAnnotationType(node.returnType)
1317
+ typeAnnotation: transformTypeAnnotationType(node.returnType)
1081
1318
  },
1082
1319
  typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
1083
1320
  };
@@ -1089,7 +1326,7 @@ const getTransforms = (code, scopeManager) => {
1089
1326
  name: node.name == null ? `$$PARAM_${idx}$$` : node.name.name,
1090
1327
  typeAnnotation: {
1091
1328
  type: 'TSTypeAnnotation',
1092
- typeAnnotation: transform.TypeAnnotationType(node.typeAnnotation)
1329
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation)
1093
1330
  },
1094
1331
  optional: node.optional
1095
1332
  };
@@ -1129,7 +1366,7 @@ const getTransforms = (code, scopeManager) => {
1129
1366
  const res = [];
1130
1367
 
1131
1368
  for (const param of node.typeParameters.params) {
1132
- res.push(transform.TypeAnnotationType(param));
1369
+ res.push(transformTypeAnnotationType(param));
1133
1370
  }
1134
1371
 
1135
1372
  return res;
@@ -1168,7 +1405,7 @@ const getTransforms = (code, scopeManager) => {
1168
1405
  type PropType = ReturnType<ExtractPropType<Obj>>; // number
1169
1406
  ```
1170
1407
  */
1171
- throw unsupportedTranslationError(node, fullTypeName);
1408
+ return unsupportedAnnotation(node, fullTypeName);
1172
1409
  }
1173
1410
 
1174
1411
  case '$Diff':
@@ -1344,86 +1581,212 @@ const getTransforms = (code, scopeManager) => {
1344
1581
  };
1345
1582
  }
1346
1583
 
1347
- case '$Shape':
1348
- case '$Partial':
1349
- {
1350
- // `$Partial<T>` => `Partial<T>`
1584
+ case '$Shape':
1585
+ case '$Partial':
1586
+ {
1587
+ // `$Partial<T>` => `Partial<T>`
1588
+ return {
1589
+ type: 'TSTypeReference',
1590
+ typeName: {
1591
+ type: 'Identifier',
1592
+ name: 'Partial'
1593
+ },
1594
+ typeParameters: {
1595
+ type: 'TSTypeParameterInstantiation',
1596
+ params: assertHasExactlyNTypeParameters(1)
1597
+ }
1598
+ };
1599
+ }
1600
+
1601
+ case '$Values':
1602
+ {
1603
+ // `$Values<T>` => `T[keyof T]`
1604
+ const transformedType = assertHasExactlyNTypeParameters(1)[0];
1605
+ return {
1606
+ type: 'TSIndexedAccessType',
1607
+ objectType: transformedType,
1608
+ indexType: {
1609
+ type: 'TSTypeOperator',
1610
+ operator: 'keyof',
1611
+ typeAnnotation: transformedType
1612
+ }
1613
+ };
1614
+ }
1615
+
1616
+ case 'Class':
1617
+ {
1618
+ // `Class<T>` => `new (...args: any[]) => T`
1619
+ const param = assertHasExactlyNTypeParameters(1)[0];
1620
+
1621
+ if (param.type !== 'TSTypeReference') {
1622
+ throw translationError(node, 'Expected a type reference within Class<T>');
1623
+ }
1624
+
1625
+ return {
1626
+ type: 'TSConstructorType',
1627
+ abstract: false,
1628
+ params: [{
1629
+ type: 'RestElement',
1630
+ argument: {
1631
+ type: 'Identifier',
1632
+ name: 'args'
1633
+ },
1634
+ typeAnnotation: {
1635
+ type: 'TSTypeAnnotation',
1636
+ typeAnnotation: {
1637
+ type: 'TSArrayType',
1638
+ elementType: {
1639
+ type: 'TSAnyKeyword'
1640
+ }
1641
+ }
1642
+ }
1643
+ }],
1644
+ returnType: {
1645
+ type: 'TSTypeAnnotation',
1646
+ typeAnnotation: param
1647
+ }
1648
+ };
1649
+ }
1650
+ } // React special conversion:
1651
+
1652
+
1653
+ const validReactImportOrGlobal = isValidReactImportOrGlobal(baseId);
1654
+ const reactImport = isReactImport(baseId);
1655
+
1656
+ if (validReactImportOrGlobal || reactImport) {
1657
+ // Returns appropriate Identifier for `React` import.
1658
+ // If a global is in use, set a flag to indicate that we should add the import.
1659
+ const getReactIdentifier = () => {
1660
+ if (shouldAddReactImport !== false) {
1661
+ shouldAddReactImport = !reactImport;
1662
+ }
1663
+
1664
+ return {
1665
+ type: 'Identifier',
1666
+ name: `React`
1667
+ };
1668
+ };
1669
+
1670
+ switch (fullTypeName) {
1671
+ // TODO: In flow this is `ChildrenArray<T> = T | $ReadOnlyArray<ChildrenArray<T>>`.
1672
+ // The recursive nature of it is rarely needed, so we're simplifying this for now
1673
+ // but omitting that aspect. Once we're able to provide utility types for our translations,
1674
+ // we should update this.
1675
+ // React.ChildrenArray<T> -> T | ReadonlyArray<T>
1676
+ // React$ChildrenArray<T> -> T | ReadonlyArray<T>
1677
+ case 'React.ChildrenArray':
1678
+ case 'React$ChildrenArray':
1679
+ {
1680
+ const [param] = assertHasExactlyNTypeParameters(1);
1681
+ return {
1682
+ type: 'TSUnionType',
1683
+ types: [param, {
1684
+ type: 'TSTypeReference',
1685
+ typeName: {
1686
+ type: 'Identifier',
1687
+ name: 'ReadonlyArray'
1688
+ },
1689
+ typeParameters: {
1690
+ type: 'TSTypeParameterInstantiation',
1691
+ params: [param]
1692
+ }
1693
+ }]
1694
+ };
1695
+ }
1696
+ // React.Component<A,B> -> React.Component<A,B>
1697
+ // React$Component<A,B> -> React.Component<A,B>
1698
+
1699
+ case 'React.Component':
1700
+ case 'React$Component':
1701
+ {
1702
+ const typeParameters = node.typeParameters;
1703
+
1704
+ if (typeParameters == null || typeParameters.params.length === 0) {
1705
+ throw translationError(node, `Expected at least 1 type parameter with \`${fullTypeName}\``);
1706
+ }
1707
+
1708
+ const params = typeParameters.params;
1709
+
1710
+ if (params.length > 2) {
1711
+ throw translationError(node, `Expected at no more than 2 type parameters with \`${fullTypeName}\``);
1712
+ }
1713
+
1714
+ return {
1715
+ type: 'TSTypeReference',
1716
+ typeName: {
1717
+ type: 'TSQualifiedName',
1718
+ left: getReactIdentifier(),
1719
+ right: {
1720
+ type: 'Identifier',
1721
+ name: 'Component'
1722
+ }
1723
+ },
1724
+ typeParameters: {
1725
+ type: 'TSTypeParameterInstantiation',
1726
+ params: params.map(param => transformTypeAnnotationType(param))
1727
+ }
1728
+ };
1729
+ }
1730
+ // React.Context<A> -> React.Context<A>
1731
+ // React$Context<A> -> React.Context<A>
1732
+
1733
+ case 'React$Context':
1734
+ case 'React.Context':
1351
1735
  return {
1352
1736
  type: 'TSTypeReference',
1353
1737
  typeName: {
1354
- type: 'Identifier',
1355
- name: 'Partial'
1738
+ type: 'TSQualifiedName',
1739
+ left: getReactIdentifier(),
1740
+ right: {
1741
+ type: 'Identifier',
1742
+ name: `Context`
1743
+ }
1356
1744
  },
1357
1745
  typeParameters: {
1358
1746
  type: 'TSTypeParameterInstantiation',
1359
1747
  params: assertHasExactlyNTypeParameters(1)
1360
1748
  }
1361
1749
  };
1362
- }
1363
-
1364
- case '$Subtype':
1365
- case '$Supertype':
1366
- {
1367
- // These types are deprecated and shouldn't be used in any modern code
1368
- // so let's not even bother trying to figure it out
1369
- throw unsupportedTranslationError(node, fullTypeName);
1370
- }
1371
-
1372
- case '$Values':
1373
- {
1374
- // `$Values<T>` => `T[keyof T]`
1375
- const transformedType = assertHasExactlyNTypeParameters(1)[0];
1376
- return {
1377
- type: 'TSIndexedAccessType',
1378
- objectType: transformedType,
1379
- indexType: {
1380
- type: 'TSTypeOperator',
1381
- operator: 'keyof',
1382
- typeAnnotation: transformedType
1383
- }
1384
- };
1385
- }
1386
-
1387
- case 'Class':
1388
- {
1389
- // `Class<T>` => `new (...args: any[]) => T`
1390
- const param = assertHasExactlyNTypeParameters(1)[0];
1391
-
1392
- if (param.type !== 'TSTypeReference') {
1393
- throw translationError(node, 'Expected a type reference within Class<T>');
1394
- }
1750
+ // React.Key -> React.Key
1751
+ // React$Key -> React.Key
1395
1752
 
1753
+ case 'React.Key':
1754
+ case 'React$Key':
1755
+ assertHasExactlyNTypeParameters(0);
1396
1756
  return {
1397
- type: 'TSConstructorType',
1398
- abstract: false,
1399
- params: [{
1400
- type: 'RestElement',
1401
- argument: {
1757
+ type: 'TSTypeReference',
1758
+ typeName: {
1759
+ type: 'TSQualifiedName',
1760
+ left: getReactIdentifier(),
1761
+ right: {
1402
1762
  type: 'Identifier',
1403
- name: 'args'
1404
- },
1405
- typeAnnotation: {
1406
- type: 'TSTypeAnnotation',
1407
- typeAnnotation: {
1408
- type: 'TSArrayType',
1409
- elementType: {
1410
- type: 'TSAnyKeyword'
1411
- }
1412
- }
1763
+ name: 'Key'
1413
1764
  }
1414
- }],
1415
- returnType: {
1416
- type: 'TSTypeAnnotation',
1417
- typeAnnotation: param
1418
1765
  }
1419
1766
  };
1420
- }
1421
- } // React special conversion:
1767
+ // React.ElementType -> React.ElementType
1768
+ // React$ElementType -> React.ElementType
1422
1769
 
1423
-
1424
- if (isReactImport(baseId)) {
1425
- switch (fullTypeName) {
1770
+ case 'React$ElementType':
1771
+ case 'React.ElementType':
1772
+ {
1773
+ assertHasExactlyNTypeParameters(0);
1774
+ return {
1775
+ type: 'TSTypeReference',
1776
+ typeName: {
1777
+ type: 'TSQualifiedName',
1778
+ left: getReactIdentifier(),
1779
+ right: {
1780
+ type: 'Identifier',
1781
+ name: `ElementType`
1782
+ }
1783
+ },
1784
+ typeParameters: undefined
1785
+ };
1786
+ }
1426
1787
  // React.Node -> React.ReactNode
1788
+
1789
+ case 'React$Node':
1427
1790
  case 'React.Node':
1428
1791
  {
1429
1792
  assertHasExactlyNTypeParameters(0);
@@ -1431,7 +1794,7 @@ const getTransforms = (code, scopeManager) => {
1431
1794
  type: 'TSTypeReference',
1432
1795
  typeName: {
1433
1796
  type: 'TSQualifiedName',
1434
- left: transform.Identifier(baseId, false),
1797
+ left: getReactIdentifier(),
1435
1798
  right: {
1436
1799
  type: 'Identifier',
1437
1800
  name: `ReactNode`
@@ -1442,13 +1805,14 @@ const getTransforms = (code, scopeManager) => {
1442
1805
  }
1443
1806
  // React.Element<typeof Component> -> React.ReactElement<typeof Component>
1444
1807
 
1808
+ case 'React$Element':
1445
1809
  case 'React.Element':
1446
1810
  {
1447
1811
  return {
1448
1812
  type: 'TSTypeReference',
1449
1813
  typeName: {
1450
1814
  type: 'TSQualifiedName',
1451
- left: transform.Identifier(baseId, false),
1815
+ left: getReactIdentifier(),
1452
1816
  right: {
1453
1817
  type: 'Identifier',
1454
1818
  name: `ReactElement`
@@ -1460,8 +1824,46 @@ const getTransforms = (code, scopeManager) => {
1460
1824
  }
1461
1825
  };
1462
1826
  }
1827
+ // React.ElementRef<typeof Component> -> React.ElementRef<typeof Component>
1828
+ // React$ElementRef<typeof Component> -> React.ElementRef<typeof Component>
1829
+
1830
+ case 'React$ElementRef':
1831
+ case 'React.ElementRef':
1832
+ return {
1833
+ type: 'TSTypeReference',
1834
+ typeName: {
1835
+ type: 'TSQualifiedName',
1836
+ left: getReactIdentifier(),
1837
+ right: {
1838
+ type: 'Identifier',
1839
+ name: `ElementRef`
1840
+ }
1841
+ },
1842
+ typeParameters: {
1843
+ type: 'TSTypeParameterInstantiation',
1844
+ params: assertHasExactlyNTypeParameters(1)
1845
+ }
1846
+ };
1847
+ // React$Fragment -> React.Fragment
1848
+ // React.Fragment -> React.Fragment
1849
+
1850
+ case 'React$FragmentType':
1851
+ case 'React.Fragment':
1852
+ assertHasExactlyNTypeParameters(0);
1853
+ return {
1854
+ type: 'TSTypeReference',
1855
+ typeName: {
1856
+ type: 'TSQualifiedName',
1857
+ left: getReactIdentifier(),
1858
+ right: {
1859
+ type: 'Identifier',
1860
+ name: `Fragment`
1861
+ }
1862
+ }
1863
+ };
1463
1864
  // React.MixedElement -> JSX.Element
1464
1865
 
1866
+ case 'React$MixedElement':
1465
1867
  case 'React.MixedElement':
1466
1868
  {
1467
1869
  assertHasExactlyNTypeParameters(0);
@@ -1481,10 +1883,35 @@ const getTransforms = (code, scopeManager) => {
1481
1883
  typeParameters: undefined
1482
1884
  };
1483
1885
  }
1484
- // React.AbstractComponent<Config> -> React.ForwardRefExoticComponent<Config>
1485
- // React.AbstractComponent<Config, Instance> -> React.ForwardRefExoticComponent<Config & React.RefAttributes<Instance>>
1886
+ // React.ComponentType<Config> -> React.ComponentType<Config>
1887
+ // React$ComponentType<Config> -> React.ComponentType<Config>
1888
+
1889
+ case 'React.ComponentType':
1890
+ case 'React$ComponentType':
1891
+ {
1892
+ return {
1893
+ type: 'TSTypeReference',
1894
+ typeName: {
1895
+ type: 'TSQualifiedName',
1896
+ left: getReactIdentifier(),
1897
+ right: {
1898
+ type: 'Identifier',
1899
+ name: 'ComponentType'
1900
+ }
1901
+ },
1902
+ typeParameters: {
1903
+ type: 'TSTypeParameterInstantiation',
1904
+ params: assertHasExactlyNTypeParameters(1)
1905
+ }
1906
+ };
1907
+ }
1908
+ // React.AbstractComponent<Config> -> React.ComponentType<Config>
1909
+ // React$AbstractComponent<Config> -> React.ComponentType<Config>
1910
+ // React.AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
1911
+ // React$AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
1486
1912
 
1487
1913
  case 'React.AbstractComponent':
1914
+ case 'React$AbstractComponent':
1488
1915
  {
1489
1916
  const typeParameters = node.typeParameters;
1490
1917
 
@@ -1498,12 +1925,15 @@ const getTransforms = (code, scopeManager) => {
1498
1925
  throw translationError(node, `Expected at no more than 2 type parameters with \`${fullTypeName}\``);
1499
1926
  }
1500
1927
 
1501
- let newTypeParam = transform.TypeAnnotationType(params[0]);
1928
+ const newParams = (() => {
1929
+ if (params.length === 1) {
1930
+ return assertHasExactlyNTypeParameters(1);
1931
+ }
1502
1932
 
1503
- if (params[1] != null) {
1504
- newTypeParam = {
1933
+ const [props, ref] = assertHasExactlyNTypeParameters(2);
1934
+ return [{
1505
1935
  type: 'TSIntersectionType',
1506
- types: [newTypeParam, {
1936
+ types: [props, {
1507
1937
  type: 'TSTypeReference',
1508
1938
  typeName: {
1509
1939
  type: 'TSQualifiedName',
@@ -1518,28 +1948,108 @@ const getTransforms = (code, scopeManager) => {
1518
1948
  },
1519
1949
  typeParameters: {
1520
1950
  type: 'TSTypeParameterInstantiation',
1521
- params: [transform.TypeAnnotationType(params[1])]
1951
+ params: [ref]
1522
1952
  }
1523
1953
  }]
1524
- };
1525
- }
1954
+ }];
1955
+ })();
1956
+
1957
+ return {
1958
+ type: 'TSTypeReference',
1959
+ typeName: {
1960
+ type: 'TSQualifiedName',
1961
+ left: getReactIdentifier(),
1962
+ right: {
1963
+ type: 'Identifier',
1964
+ name: 'ComponentType'
1965
+ }
1966
+ },
1967
+ typeParameters: {
1968
+ type: 'TSTypeParameterInstantiation',
1969
+ params: newParams
1970
+ }
1971
+ };
1972
+ }
1973
+ // React.ElementConfig<A> -> JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
1974
+ // React$ElementConfig<A> -> JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
1526
1975
 
1976
+ case 'React.ElementConfig':
1977
+ case 'React$ElementConfig':
1978
+ {
1979
+ const [param] = assertHasExactlyNTypeParameters(1);
1527
1980
  return {
1528
1981
  type: 'TSTypeReference',
1529
1982
  typeName: {
1530
1983
  type: 'TSQualifiedName',
1531
- left: transform.Identifier(baseId, false),
1984
+ left: {
1985
+ type: 'Identifier',
1986
+ name: 'JSX'
1987
+ },
1532
1988
  right: {
1533
1989
  type: 'Identifier',
1534
- name: `ForwardRefExoticComponent`
1990
+ name: 'LibraryManagedAttributes'
1535
1991
  }
1536
1992
  },
1537
1993
  typeParameters: {
1538
1994
  type: 'TSTypeParameterInstantiation',
1539
- params: [newTypeParam]
1995
+ params: [param, {
1996
+ type: 'TSTypeReference',
1997
+ typeName: {
1998
+ type: 'TSQualifiedName',
1999
+ left: getReactIdentifier(),
2000
+ right: {
2001
+ type: 'Identifier',
2002
+ name: `ComponentProps`
2003
+ }
2004
+ },
2005
+ typeParameters: {
2006
+ type: 'TSTypeParameterInstantiation',
2007
+ params: [param]
2008
+ }
2009
+ }]
1540
2010
  }
1541
2011
  };
1542
2012
  }
2013
+ // React.Ref<C> -> NonNullable<React.Ref<C> | string | number>
2014
+ // React$Ref<C> -> NonNullable<React.Ref<C> | string | number>
2015
+
2016
+ case 'React.Ref':
2017
+ case 'React$Ref':
2018
+ return {
2019
+ type: 'TSTypeReference',
2020
+ typeName: {
2021
+ type: 'Identifier',
2022
+ name: 'NonNullable'
2023
+ },
2024
+ typeParameters: {
2025
+ type: 'TSTypeParameterInstantiation',
2026
+ params: [{
2027
+ type: 'TSUnionType',
2028
+ types: [{
2029
+ type: 'TSTypeReference',
2030
+ typeName: {
2031
+ type: 'TSQualifiedName',
2032
+ left: getReactIdentifier(),
2033
+ right: {
2034
+ type: 'Identifier',
2035
+ name: 'Ref'
2036
+ }
2037
+ },
2038
+ typeParameters: {
2039
+ type: 'TSTypeParameterInstantiation',
2040
+ params: assertHasExactlyNTypeParameters(1)
2041
+ }
2042
+ }, {
2043
+ type: 'TSStringKeyword'
2044
+ }, {
2045
+ type: 'TSNumberKeyword'
2046
+ }]
2047
+ }]
2048
+ }
2049
+ };
2050
+
2051
+ default:
2052
+ return unsupportedAnnotation(node, fullTypeName);
1543
2053
  }
1544
2054
  }
1545
2055
 
@@ -1563,8 +2073,8 @@ const getTransforms = (code, scopeManager) => {
1563
2073
  IndexedAccessType(node) {
1564
2074
  return {
1565
2075
  type: 'TSIndexedAccessType',
1566
- objectType: transform.TypeAnnotationType(node.objectType),
1567
- indexType: transform.TypeAnnotationType(node.indexType)
2076
+ objectType: transformTypeAnnotationType(node.objectType),
2077
+ indexType: transformTypeAnnotationType(node.indexType)
1568
2078
  };
1569
2079
  },
1570
2080
 
@@ -1581,65 +2091,63 @@ const getTransforms = (code, scopeManager) => {
1581
2091
  },
1582
2092
 
1583
2093
  ImportDeclaration(node) {
1584
- if (node.importKind === 'typeof') {
1585
- /*
1586
- TODO - this is a complicated change to support because TS
1587
- does not have typeof imports.
1588
- Making it a `type` import would change the meaning!
1589
- The only way to truly support this is to prefix all **usages** with `typeof T`.
1590
- eg:
1591
- ```
1592
- import typeof Foo from 'Foo';
1593
- type T = Foo;
1594
- ```
1595
- would become:
1596
- ```
1597
- import type Foo from 'Foo';
1598
- type T = typeof Foo;
1599
- ```
1600
- This seems simple, but will actually be super complicated for us to do with
1601
- our current translation architecture
1602
- */
1603
- throw unsupportedTranslationError(node, 'typeof imports');
1604
- }
1605
-
1606
2094
  const importKind = node.importKind;
1607
- return {
1608
- type: 'ImportDeclaration',
1609
- assertions: node.assertions.map(transform.ImportAttribute),
1610
- importKind: importKind != null ? importKind : 'value',
1611
- source: transform.StringLiteral(node.source),
1612
- specifiers: node.specifiers.map(spec => {
1613
- var _spec$importKind;
1614
-
1615
- switch (spec.type) {
1616
- case 'ImportDefaultSpecifier':
1617
- return {
1618
- type: 'ImportDefaultSpecifier',
1619
- local: transform.Identifier(spec.local, false)
1620
- };
2095
+ const specifiers = [];
2096
+ const unsupportedSpecifiers = [];
2097
+ node.specifiers.forEach(spec => {
2098
+ let id = (() => {
2099
+ if (node.importKind === 'typeof' || spec.importKind === 'typeof') {
2100
+ const id = {
2101
+ type: 'Identifier',
2102
+ name: getPlaceholderNameForTypeofImport()
2103
+ };
2104
+ unsupportedSpecifiers.push({
2105
+ type: 'TSTypeAliasDeclaration',
2106
+ id: transform.Identifier(spec.local, false),
2107
+ typeAnnotation: {
2108
+ type: 'TSTypeQuery',
2109
+ exprName: id
2110
+ }
2111
+ });
2112
+ return id;
2113
+ }
1621
2114
 
1622
- case 'ImportNamespaceSpecifier':
1623
- return {
1624
- type: 'ImportNamespaceSpecifier',
1625
- local: transform.Identifier(spec.local, false)
1626
- };
2115
+ return transform.Identifier(spec.local, false);
2116
+ })();
1627
2117
 
1628
- case 'ImportSpecifier':
1629
- if (spec.importKind === 'typeof') {
1630
- // see above
1631
- throw unsupportedTranslationError(node, 'typeof imports');
1632
- }
2118
+ switch (spec.type) {
2119
+ case 'ImportDefaultSpecifier':
2120
+ specifiers.push({
2121
+ type: 'ImportDefaultSpecifier',
2122
+ local: id
2123
+ });
2124
+ return;
1633
2125
 
1634
- return {
1635
- type: 'ImportSpecifier',
1636
- importKind: (_spec$importKind = spec.importKind) != null ? _spec$importKind : 'value',
1637
- imported: transform.Identifier(spec.imported, false),
1638
- local: transform.Identifier(spec.local, false)
1639
- };
1640
- }
1641
- })
1642
- };
2126
+ case 'ImportNamespaceSpecifier':
2127
+ specifiers.push({
2128
+ type: 'ImportNamespaceSpecifier',
2129
+ local: id
2130
+ });
2131
+ return;
2132
+
2133
+ case 'ImportSpecifier':
2134
+ specifiers.push({
2135
+ type: 'ImportSpecifier',
2136
+ importKind: spec.importKind === 'typeof' || spec.importKind === 'type' ? 'type' : null,
2137
+ imported: transform.Identifier(spec.imported, false),
2138
+ local: id
2139
+ });
2140
+ return;
2141
+ }
2142
+ });
2143
+ const out = specifiers.length ? [{
2144
+ type: 'ImportDeclaration',
2145
+ assertions: node.assertions.map(transform.ImportAttribute),
2146
+ importKind: importKind === 'typeof' ? 'type' : importKind != null ? importKind : 'value',
2147
+ source: transform.StringLiteral(node.source),
2148
+ specifiers
2149
+ }] : [];
2150
+ return [...out, ...unsupportedSpecifiers];
1643
2151
  },
1644
2152
 
1645
2153
  InterfaceExtends(node) {
@@ -1671,7 +2179,7 @@ const getTransforms = (code, scopeManager) => {
1671
2179
  IntersectionTypeAnnotation(node) {
1672
2180
  return {
1673
2181
  type: 'TSIntersectionType',
1674
- types: node.types.map(transform.TypeAnnotationType)
2182
+ types: node.types.map(transformTypeAnnotationType)
1675
2183
  };
1676
2184
  },
1677
2185
 
@@ -1726,7 +2234,7 @@ const getTransforms = (code, scopeManager) => {
1726
2234
  type: 'TSNullKeyword'
1727
2235
  }, {
1728
2236
  type: 'TSUndefinedKeyword'
1729
- }, transform.TypeAnnotationType(node.typeAnnotation)]
2237
+ }, transformTypeAnnotationType(node.typeAnnotation)]
1730
2238
  };
1731
2239
  },
1732
2240
 
@@ -1783,7 +2291,7 @@ const getTransforms = (code, scopeManager) => {
1783
2291
 
1784
2292
 
1785
2293
  if (node.internalSlots.length > 0) {
1786
- throw unsupportedTranslationError(node.internalSlots[0], 'internal slots');
2294
+ return unsupportedAnnotation(node.internalSlots[0], 'internal slots');
1787
2295
  }
1788
2296
 
1789
2297
  if (!node.properties.find(FlowESTree.isObjectTypeSpreadProperty)) {
@@ -1794,6 +2302,11 @@ const getTransforms = (code, scopeManager) => {
1794
2302
  throw unexpectedTranslationError(property, 'Impossible state');
1795
2303
  }
1796
2304
 
2305
+ if (property.type === 'ObjectTypeMappedTypeProperty') {
2306
+ // TODO - Support mapped types
2307
+ return unsupportedAnnotation(property, 'object type with mapped type property');
2308
+ }
2309
+
1797
2310
  members.push({
1798
2311
  start: property.range[0],
1799
2312
  node: transform.ObjectTypeProperty(property)
@@ -1870,7 +2383,7 @@ const getTransforms = (code, scopeManager) => {
1870
2383
  - spreads of things that aren't "Identifiers"
1871
2384
  */
1872
2385
  if (members.length > 0) {
1873
- throw unsupportedTranslationError(node, 'object types with spreads, indexers and/or call properties at the same time');
2386
+ return unsupportedAnnotation(node, 'object types with spreads, indexers and/or call properties at the same time');
1874
2387
  }
1875
2388
 
1876
2389
  const typesToIntersect = [];
@@ -1878,16 +2391,19 @@ const getTransforms = (code, scopeManager) => {
1878
2391
  for (const property of node.properties) {
1879
2392
  if (property.type === 'ObjectTypeSpreadProperty') {
1880
2393
  if (members.length > 0) {
1881
- throw unsupportedTranslationError(property, 'object types with spreads in the middle or at the end');
2394
+ return unsupportedAnnotation(property, 'object types with spreads in the middle or at the end');
1882
2395
  }
1883
2396
 
1884
- const spreadType = transform.TypeAnnotationType(property.argument);
2397
+ const spreadType = transformTypeAnnotationType(property.argument);
1885
2398
 
1886
2399
  if (spreadType.type !== 'TSTypeReference') {
1887
- throw unsupportedTranslationError(property, 'object types with complex spreads');
2400
+ return unsupportedAnnotation(property, 'object types with complex spreads');
1888
2401
  }
1889
2402
 
1890
2403
  typesToIntersect.push(spreadType);
2404
+ } else if (property.type === 'ObjectTypeMappedTypeProperty') {
2405
+ // TODO - Support mapped types
2406
+ return unsupportedAnnotation(property, 'object type with mapped type property');
1891
2407
  } else {
1892
2408
  members.push({
1893
2409
  start: property.range[0],
@@ -1957,14 +2473,14 @@ const getTransforms = (code, scopeManager) => {
1957
2473
  name: node.id == null ? '$$Key$$' : node.id.name,
1958
2474
  typeAnnotation: {
1959
2475
  type: 'TSTypeAnnotation',
1960
- typeAnnotation: transform.TypeAnnotationType(node.key)
2476
+ typeAnnotation: transformTypeAnnotationType(node.key)
1961
2477
  }
1962
2478
  }],
1963
2479
  readonly: ((_node$variance = node.variance) == null ? void 0 : _node$variance.kind) === 'plus',
1964
2480
  static: node.static,
1965
2481
  typeAnnotation: {
1966
2482
  type: 'TSTypeAnnotation',
1967
- typeAnnotation: transform.TypeAnnotationType(node.value)
2483
+ typeAnnotation: transformTypeAnnotationType(node.value)
1968
2484
  }
1969
2485
  };
1970
2486
  },
@@ -2019,7 +2535,7 @@ const getTransforms = (code, scopeManager) => {
2019
2535
  static: node.static,
2020
2536
  typeAnnotation: {
2021
2537
  type: 'TSTypeAnnotation',
2022
- typeAnnotation: transform.TypeAnnotationType(node.value)
2538
+ typeAnnotation: transformTypeAnnotationType(node.value)
2023
2539
  }
2024
2540
  };
2025
2541
  },
@@ -2048,10 +2564,10 @@ const getTransforms = (code, scopeManager) => {
2048
2564
  },
2049
2565
  typeParameters: {
2050
2566
  type: 'TSTypeParameterInstantiation',
2051
- params: [transform.TypeAnnotationType(node.objectType)]
2567
+ params: [transformTypeAnnotationType(node.objectType)]
2052
2568
  }
2053
2569
  },
2054
- indexType: transform.TypeAnnotationType(node.indexType)
2570
+ indexType: transformTypeAnnotationType(node.indexType)
2055
2571
  };
2056
2572
  },
2057
2573
 
@@ -2064,6 +2580,15 @@ const getTransforms = (code, scopeManager) => {
2064
2580
  };
2065
2581
  },
2066
2582
 
2583
+ QualifiedTypeofIdentifier(node) {
2584
+ const qual = node.qualification;
2585
+ return {
2586
+ type: 'TSQualifiedName',
2587
+ left: qual.type === 'Identifier' ? transform.Identifier(qual, false) : transform.QualifiedTypeofIdentifier(qual),
2588
+ right: transform.Identifier(node.id, false)
2589
+ };
2590
+ },
2591
+
2067
2592
  RegExpLiteral(node) {
2068
2593
  return {
2069
2594
  type: 'Literal',
@@ -2116,7 +2641,7 @@ const getTransforms = (code, scopeManager) => {
2116
2641
  TupleTypeAnnotation(node) {
2117
2642
  return {
2118
2643
  type: 'TSTupleType',
2119
- elementTypes: node.types.map(transform.TypeAnnotationType)
2644
+ elementTypes: node.types.map(transformTypeAnnotationType)
2120
2645
  };
2121
2646
  },
2122
2647
 
@@ -2127,116 +2652,26 @@ const getTransforms = (code, scopeManager) => {
2127
2652
  TypeAnnotation(node) {
2128
2653
  return {
2129
2654
  type: 'TSTypeAnnotation',
2130
- typeAnnotation: transform.TypeAnnotationType(node.typeAnnotation)
2655
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation)
2131
2656
  };
2132
2657
  },
2133
2658
 
2134
- TypeAnnotationType(node) {
2135
- switch (node.type) {
2136
- case 'AnyTypeAnnotation':
2137
- return transform.AnyTypeAnnotation(node);
2138
-
2139
- case 'ArrayTypeAnnotation':
2140
- return transform.ArrayTypeAnnotation(node);
2141
-
2142
- case 'BigIntLiteralTypeAnnotation':
2143
- return transform.BigIntLiteralTypeAnnotation(node);
2144
-
2145
- case 'BigIntTypeAnnotation':
2146
- return transform.BigIntTypeAnnotation(node);
2147
-
2148
- case 'BooleanLiteralTypeAnnotation':
2149
- return transform.BooleanLiteralTypeAnnotation(node);
2150
-
2151
- case 'BooleanTypeAnnotation':
2152
- return transform.BooleanTypeAnnotation(node);
2153
-
2154
- case 'EmptyTypeAnnotation':
2155
- return transform.EmptyTypeAnnotation(node);
2156
-
2157
- case 'ExistsTypeAnnotation':
2158
- return transform.ExistsTypeAnnotation(node);
2159
-
2160
- case 'FunctionTypeAnnotation':
2161
- return transform.FunctionTypeAnnotation(node);
2162
-
2163
- case 'GenericTypeAnnotation':
2164
- return transform.GenericTypeAnnotation(node);
2165
-
2166
- case 'IndexedAccessType':
2167
- return transform.IndexedAccessType(node);
2168
-
2169
- case 'InterfaceTypeAnnotation':
2170
- return transform.InterfaceTypeAnnotation(node);
2171
-
2172
- case 'IntersectionTypeAnnotation':
2173
- return transform.IntersectionTypeAnnotation(node);
2174
-
2175
- case 'MixedTypeAnnotation':
2176
- return transform.MixedTypeAnnotation(node);
2177
-
2178
- case 'NullLiteralTypeAnnotation':
2179
- return transform.NullLiteralTypeAnnotation(node);
2180
-
2181
- case 'NullableTypeAnnotation':
2182
- return transform.NullableTypeAnnotation(node);
2183
-
2184
- case 'NumberLiteralTypeAnnotation':
2185
- return transform.NumberLiteralTypeAnnotation(node);
2186
-
2187
- case 'NumberTypeAnnotation':
2188
- return transform.NumberTypeAnnotation(node);
2189
-
2190
- case 'ObjectTypeAnnotation':
2191
- return transform.ObjectTypeAnnotation(node);
2192
-
2193
- case 'OptionalIndexedAccessType':
2194
- return transform.OptionalIndexedAccessType(node);
2195
-
2196
- case 'QualifiedTypeIdentifier':
2197
- return transform.QualifiedTypeIdentifier(node);
2198
-
2199
- case 'StringLiteralTypeAnnotation':
2200
- return transform.StringLiteralTypeAnnotation(node);
2201
-
2202
- case 'StringTypeAnnotation':
2203
- return transform.StringTypeAnnotation(node);
2204
-
2205
- case 'SymbolTypeAnnotation':
2206
- return transform.SymbolTypeAnnotation(node);
2207
-
2208
- case 'ThisTypeAnnotation':
2209
- return transform.ThisTypeAnnotation(node);
2210
-
2211
- case 'TupleTypeAnnotation':
2212
- return transform.TupleTypeAnnotation(node);
2213
-
2214
- case 'TypeofTypeAnnotation':
2215
- return transform.TypeofTypeAnnotation(node);
2216
-
2217
- case 'UnionTypeAnnotation':
2218
- return transform.UnionTypeAnnotation(node);
2219
-
2220
- case 'VoidTypeAnnotation':
2221
- return transform.VoidTypeAnnotation(node);
2222
-
2223
- default:
2224
- throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
2225
- }
2226
- },
2227
-
2228
2659
  TypeofTypeAnnotation(node) {
2229
- const argument = transform.TypeAnnotationType(node.argument);
2660
+ switch (node.argument.type) {
2661
+ case 'Identifier':
2662
+ return {
2663
+ type: 'TSTypeQuery',
2664
+ exprName: transform.Identifier(node.argument),
2665
+ typeParameters: undefined
2666
+ };
2230
2667
 
2231
- if (argument.type !== 'TSTypeReference') {
2232
- throw unexpectedTranslationError(node, `Expected to find a type reference as the argument to the TypeofTypeAnnotation, but got ${node.argument.type}`);
2668
+ case 'QualifiedTypeofIdentifier':
2669
+ return {
2670
+ type: 'TSTypeQuery',
2671
+ exprName: transform.QualifiedTypeofIdentifier(node.argument),
2672
+ typeParameters: undefined
2673
+ };
2233
2674
  }
2234
-
2235
- return {
2236
- type: 'TSTypeQuery',
2237
- exprName: argument.typeName,
2238
- typeParameters: argument.typeParameters
2239
- };
2240
2675
  },
2241
2676
 
2242
2677
  TypeParameter(node) {
@@ -2265,8 +2700,8 @@ const getTransforms = (code, scopeManager) => {
2265
2700
  type: 'Identifier',
2266
2701
  name: node.name
2267
2702
  },
2268
- constraint: node.bound == null ? undefined : transform.TypeAnnotationType(node.bound.typeAnnotation),
2269
- default: node.default == null ? undefined : transform.TypeAnnotationType(node.default),
2703
+ constraint: node.bound == null ? undefined : transformTypeAnnotationType(node.bound.typeAnnotation),
2704
+ default: node.default == null ? undefined : transformTypeAnnotationType(node.default),
2270
2705
  in: false,
2271
2706
  out: false // in: variance.has('in'),
2272
2707
  // out: variance.has('out'),
@@ -2284,14 +2719,14 @@ const getTransforms = (code, scopeManager) => {
2284
2719
  TypeParameterInstantiation(node) {
2285
2720
  return {
2286
2721
  type: 'TSTypeParameterInstantiation',
2287
- params: node.params.map(transform.TypeAnnotationType)
2722
+ params: node.params.map(transformTypeAnnotationType)
2288
2723
  };
2289
2724
  },
2290
2725
 
2291
2726
  UnionTypeAnnotation(node) {
2292
2727
  return {
2293
2728
  type: 'TSUnionType',
2294
- types: node.types.map(transform.TypeAnnotationType)
2729
+ types: node.types.map(transformTypeAnnotationType)
2295
2730
  };
2296
2731
  },
2297
2732
 
@@ -2321,5 +2756,5 @@ const getTransforms = (code, scopeManager) => {
2321
2756
  };
2322
2757
  }
2323
2758
 
2324
- return transform;
2759
+ return [transform, code];
2325
2760
  };