flow-api-translator 0.28.1 → 0.29.1

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.
@@ -0,0 +1,3853 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ * @format
9
+ */
10
+ 'use strict';
11
+
12
+ Object.defineProperty(exports, "__esModule", {
13
+ value: true
14
+ });
15
+ exports.flowDefToTSDef = flowDefToTSDef;
16
+
17
+ var FlowESTree = _interopRequireWildcard(require("hermes-estree"));
18
+
19
+ var TSESTree = _interopRequireWildcard(require("./utils/ts-estree-ast-types"));
20
+
21
+ var _hermesTransform = require("hermes-transform");
22
+
23
+ var _ErrorUtils = require("./utils/ErrorUtils");
24
+
25
+ var _DocblockUtils = require("./utils/DocblockUtils");
26
+
27
+ var _os = require("os");
28
+
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); }
30
+
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; }
32
+
33
+ const DUMMY_LOC = {
34
+ start: {
35
+ line: 1,
36
+ column: 0
37
+ },
38
+ end: {
39
+ line: 1,
40
+ column: 0
41
+ }
42
+ };
43
+
44
+ function constructFlowNode(node) {
45
+ return node;
46
+ }
47
+
48
+ const cloneJSDocCommentsToNewNode = // $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
49
+ _hermesTransform.cloneJSDocCommentsToNewNode;
50
+ const makeCommentOwnLine = // $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
51
+ _hermesTransform.makeCommentOwnLine;
52
+ const VALID_REACT_IMPORTS = new Set(['React', 'react']);
53
+
54
+ function isValidReactImportOrGlobal(id) {
55
+ return VALID_REACT_IMPORTS.has(id.name) || id.name.startsWith('React$');
56
+ }
57
+
58
+ let shouldAddReactImport = null; // Returns appropriate Identifier for `React` import.
59
+ // If a global is in use, set a flag to indicate that we should add the import.
60
+
61
+ function getReactIdentifier(hasReactImport) {
62
+ if (shouldAddReactImport !== false) {
63
+ shouldAddReactImport = !hasReactImport;
64
+ }
65
+
66
+ return {
67
+ type: 'Identifier',
68
+ loc: DUMMY_LOC,
69
+ name: `React`
70
+ };
71
+ }
72
+
73
+ function flowDefToTSDef(originalCode, ast, scopeManager, opts) {
74
+ const tsBody = [];
75
+ const tsProgram = {
76
+ type: 'Program',
77
+ body: tsBody,
78
+ sourceType: ast.sourceType,
79
+ loc: ast.loc,
80
+ docblock: ast.docblock == null ? null : (0, _DocblockUtils.removeAtFlowFromDocblock)(ast.docblock)
81
+ };
82
+ shouldAddReactImport = null;
83
+ const [transform, code] = getTransforms(originalCode, scopeManager, opts);
84
+
85
+ for (const node of ast.body) {
86
+ if (node.type in transform) {
87
+ const result = transform[// $FlowExpectedError[prop-missing]
88
+ node.type]( // $FlowExpectedError[incompatible-type]
89
+ // $FlowExpectedError[prop-missing]
90
+ // $FlowFixMe[incompatible-call]
91
+ node);
92
+ tsBody.push(...(Array.isArray(result) ? result : [result]));
93
+ } else {
94
+ throw (0, _ErrorUtils.unexpectedTranslationError)(node, `Unexpected node type ${node.type}`, {
95
+ code
96
+ });
97
+ }
98
+ }
99
+
100
+ if (shouldAddReactImport === true) {
101
+ tsBody.unshift({
102
+ type: 'ImportDeclaration',
103
+ assertions: [],
104
+ loc: DUMMY_LOC,
105
+ source: {
106
+ type: 'Literal',
107
+ loc: DUMMY_LOC,
108
+ value: 'react',
109
+ raw: "'react'"
110
+ },
111
+ specifiers: [{
112
+ type: 'ImportNamespaceSpecifier',
113
+ loc: DUMMY_LOC,
114
+ local: {
115
+ type: 'Identifier',
116
+ loc: DUMMY_LOC,
117
+ name: 'React'
118
+ }
119
+ }],
120
+ importKind: 'value'
121
+ });
122
+ }
123
+
124
+ return [tsProgram, code];
125
+ }
126
+
127
+ const getTransforms = (originalCode, scopeManager, opts) => {
128
+ let code = originalCode;
129
+
130
+ function translationError(node, message) {
131
+ return (0, _ErrorUtils.translationError)(node, message, {
132
+ code
133
+ });
134
+ }
135
+
136
+ function unexpectedTranslationError(node, message) {
137
+ return (0, _ErrorUtils.unexpectedTranslationError)(node, message, {
138
+ code
139
+ });
140
+ }
141
+
142
+ function unsupportedFeatureMessage(thing) {
143
+ return `Unsupported feature: Translating "${thing}" is currently not supported.`;
144
+ }
145
+
146
+ function buildCodeFrameForComment(node, message) {
147
+ return (0, _ErrorUtils.buildCodeFrame)(node, message, code, false);
148
+ }
149
+
150
+ function addErrorComment(node, message) {
151
+ var _node$comments;
152
+
153
+ const comment = {
154
+ type: 'Block',
155
+ loc: DUMMY_LOC,
156
+ value: `*${_os.EOL} * ${message.replace(new RegExp(_os.EOL, 'g'), `${_os.EOL} * `)}${_os.EOL}*`,
157
+ leading: true,
158
+ printed: false
159
+ };
160
+ code = makeCommentOwnLine(code, comment); // $FlowExpectedError[prop-missing]
161
+ // $FlowExpectedError[cannot-write]
162
+
163
+ (_node$comments = node.comments) != null ? _node$comments : node.comments = []; // $FlowExpectedError[incompatible-cast]
164
+
165
+ node.comments.push(comment);
166
+ }
167
+
168
+ function unsupportedAnnotation(node, thing) {
169
+ const message = unsupportedFeatureMessage(thing);
170
+
171
+ if (opts.recoverFromErrors) {
172
+ const codeFrame = buildCodeFrameForComment(node, message);
173
+ const newNode = {
174
+ type: 'TSAnyKeyword',
175
+ loc: DUMMY_LOC
176
+ };
177
+ addErrorComment(newNode, codeFrame);
178
+ return newNode;
179
+ }
180
+
181
+ throw translationError(node, message);
182
+ }
183
+
184
+ function unsupportedDeclaration(node, thing, id, declare = false, typeParameters = null) {
185
+ const message = unsupportedFeatureMessage(thing);
186
+
187
+ if (opts.recoverFromErrors) {
188
+ const codeFrame = buildCodeFrameForComment(node, message);
189
+ const newNode = {
190
+ type: 'TSTypeAliasDeclaration',
191
+ loc: DUMMY_LOC,
192
+ declare,
193
+ id: transform.Identifier(id, false),
194
+ typeAnnotation: {
195
+ type: 'TSAnyKeyword',
196
+ loc: DUMMY_LOC
197
+ },
198
+ typeParameters: typeParameters == null ? undefined : transform.TypeParameterDeclaration(typeParameters)
199
+ };
200
+ addErrorComment(newNode, codeFrame);
201
+ return newNode;
202
+ }
203
+
204
+ throw translationError(node, message);
205
+ }
206
+
207
+ const topScope = (() => {
208
+ const globalScope = scopeManager.globalScope;
209
+
210
+ if (globalScope.childScopes.length > 0 && globalScope.childScopes[0].type === 'module') {
211
+ return globalScope.childScopes[0];
212
+ }
213
+
214
+ return globalScope;
215
+ })();
216
+
217
+ function isReactImport(scopeNode, name) {
218
+ let currentScope = (() => {
219
+ let scope = null;
220
+ let node = scopeNode;
221
+
222
+ while (!scope && node) {
223
+ scope = scopeManager.acquire(node, true);
224
+ node = node.parent;
225
+ }
226
+
227
+ return scope;
228
+ })();
229
+
230
+ if (currentScope == null) {
231
+ throw new Error('unable to resolve scope');
232
+ }
233
+
234
+ const variableDef = (() => {
235
+ while (currentScope != null) {
236
+ for (const variable of currentScope.variables) {
237
+ if (variable.defs.length && variable.name === name) {
238
+ return variable;
239
+ }
240
+ }
241
+
242
+ currentScope = currentScope.upper;
243
+ }
244
+ })(); // No variable found, it is not imported.
245
+ // It could be a global though if isValidReactImportOrGlobal returns true.
246
+
247
+
248
+ if (variableDef == null) {
249
+ return false;
250
+ }
251
+
252
+ const def = variableDef.defs[0]; // Detect:
253
+
254
+ switch (def.type) {
255
+ // import React from 'react';
256
+ // import * as React from 'react';
257
+ case 'ImportBinding':
258
+ {
259
+ if (def.node.type === 'ImportDefaultSpecifier' || def.node.type === 'ImportNamespaceSpecifier') {
260
+ return VALID_REACT_IMPORTS.has(def.parent.source.value);
261
+ }
262
+
263
+ return false;
264
+ }
265
+ // Globals
266
+
267
+ case 'ImplicitGlobalVariable':
268
+ {
269
+ return VALID_REACT_IMPORTS.has(name);
270
+ }
271
+ // TODO Handle:
272
+ // const React = require('react');
273
+ // const Something = React;
274
+ }
275
+
276
+ return false;
277
+ }
278
+
279
+ function EnumImpl(node) {
280
+ const body = node.body;
281
+
282
+ if (body.type === 'EnumSymbolBody') {
283
+ /*
284
+ There's unfortunately no way for us to support this in a clean way.
285
+ We can get really close using this code:
286
+ ```
287
+ declare namespace SymbolEnum {
288
+ export const member1: unique symbol;
289
+ export type member1 = typeof member1;
290
+ export const member2: unique symbol;
291
+ export type member2 = typeof member2;
292
+ }
293
+ type SymbolEnum = typeof SymbolEnum[keyof typeof SymbolEnum];
294
+ ```
295
+ However as explained in https://github.com/microsoft/TypeScript/issues/43657:
296
+ "A unique symbol type is never transferred from one declaration to another through inference."
297
+ This intended behaviour in TS means that the usage of the fake-enum would look like this:
298
+ ```
299
+ const value: SymbolEnum.member1 = SymbolEnum.member1;
300
+ // ^^^^^^^^^^^^^^^^^^ required to force TS to retain the information
301
+ ```
302
+ Which is really clunky and shitty. It definitely works, but ofc it's not good.
303
+ We can go with this design if users are okay with it!
304
+ Considering how rarely used symbol enums are ATM, let's just put a pin in it for now.
305
+ */
306
+ return unsupportedDeclaration(node, 'symbol enums', node.id, FlowESTree.isDeclareEnum(node));
307
+ }
308
+
309
+ if (body.type === 'EnumBooleanBody') {
310
+ /*
311
+ TODO - TS enums only allow strings or numbers as their values - not booleans.
312
+ This means we need a non-ts-enum representation of the enum.
313
+ We can support boolean enums using a construct like this:
314
+ ```ts
315
+ declare namespace BooleanEnum {
316
+ export const member1: true;
317
+ export type member1 = typeof member1;
318
+ export const member2: false;
319
+ export type member2 = typeof member1;
320
+ }
321
+ declare type BooleanEnum = boolean;
322
+ ```
323
+ But it's pretty clunky and ugly.
324
+ Considering how rarely used boolean enums are ATM, let's just put a pin in it for now.
325
+ */
326
+ return unsupportedDeclaration(node, 'boolean enums', node.id, FlowESTree.isDeclareEnum(node));
327
+ }
328
+
329
+ const members = [];
330
+
331
+ for (const member of body.members) {
332
+ switch (member.type) {
333
+ case 'EnumDefaultedMember':
334
+ {
335
+ if (body.type === 'EnumNumberBody') {
336
+ // this should be impossible!
337
+ throw unexpectedTranslationError(member, 'Unexpected defaulted number enum member');
338
+ }
339
+
340
+ members.push({
341
+ type: 'TSEnumMember',
342
+ loc: DUMMY_LOC,
343
+ computed: false,
344
+ id: transform.Identifier(member.id, false),
345
+ initializer: {
346
+ type: 'Literal',
347
+ loc: DUMMY_LOC,
348
+ raw: `"${member.id.name}"`,
349
+ value: member.id.name
350
+ }
351
+ });
352
+ break;
353
+ }
354
+
355
+ case 'EnumNumberMember':
356
+ case 'EnumStringMember':
357
+ members.push({
358
+ type: 'TSEnumMember',
359
+ loc: DUMMY_LOC,
360
+ computed: false,
361
+ id: transform.Identifier(member.id, false),
362
+ initializer: member.init.literalType === 'string' ? transform.StringLiteral(member.init) : transform.NumericLiteral(member.init)
363
+ });
364
+ }
365
+ }
366
+
367
+ const bodyRepresentationType = body.type === 'EnumNumberBody' ? {
368
+ type: 'TSNumberKeyword',
369
+ loc: DUMMY_LOC
370
+ } : {
371
+ type: 'TSStringKeyword',
372
+ loc: DUMMY_LOC
373
+ };
374
+ const enumName = transform.Identifier(node.id, false);
375
+ return [{
376
+ type: 'TSEnumDeclaration',
377
+ loc: DUMMY_LOC,
378
+ const: false,
379
+ declare: true,
380
+ id: enumName,
381
+ members
382
+ }, // flow also exports `.cast`, `.isValid`, `.members` and `.getName` for enums
383
+ // we can use declaration merging to declare these functions on the enum:
384
+
385
+ /*
386
+ declare enum Foo {
387
+ A = 1,
388
+ B = 2,
389
+ }
390
+ declare namespace Foo {
391
+ export function cast(value: number | null | undefined): Foo;
392
+ export function isValid(value: number | null | undefined): value is Foo;
393
+ export function members(): IterableIterator<Foo>;
394
+ export function getName(value: Foo): string;
395
+ }
396
+ */
397
+ {
398
+ type: 'TSModuleDeclaration',
399
+ loc: DUMMY_LOC,
400
+ declare: true,
401
+ id: enumName,
402
+ body: {
403
+ type: 'TSModuleBlock',
404
+ loc: DUMMY_LOC,
405
+ body: [// export function cast(value: number | null | undefined): Foo
406
+ {
407
+ type: 'ExportNamedDeclaration',
408
+ loc: DUMMY_LOC,
409
+ declaration: {
410
+ type: 'TSDeclareFunction',
411
+ loc: DUMMY_LOC,
412
+ id: {
413
+ type: 'Identifier',
414
+ loc: DUMMY_LOC,
415
+ name: 'cast'
416
+ },
417
+ generator: false,
418
+ expression: false,
419
+ async: false,
420
+ params: [{
421
+ type: 'Identifier',
422
+ loc: DUMMY_LOC,
423
+ name: 'value',
424
+ typeAnnotation: {
425
+ type: 'TSTypeAnnotation',
426
+ loc: DUMMY_LOC,
427
+ typeAnnotation: {
428
+ type: 'TSUnionType',
429
+ loc: DUMMY_LOC,
430
+ types: [bodyRepresentationType, {
431
+ type: 'TSNullKeyword',
432
+ loc: DUMMY_LOC
433
+ }, {
434
+ type: 'TSUndefinedKeyword',
435
+ loc: DUMMY_LOC
436
+ }]
437
+ }
438
+ }
439
+ }],
440
+ returnType: {
441
+ type: 'TSTypeAnnotation',
442
+ loc: DUMMY_LOC,
443
+ typeAnnotation: {
444
+ type: 'TSTypeReference',
445
+ loc: DUMMY_LOC,
446
+ typeName: enumName
447
+ }
448
+ }
449
+ },
450
+ specifiers: [],
451
+ source: null,
452
+ exportKind: 'value',
453
+ assertions: []
454
+ }, // export function isValid(value: number | null | undefined): value is Foo;
455
+ {
456
+ type: 'ExportNamedDeclaration',
457
+ loc: DUMMY_LOC,
458
+ declaration: {
459
+ type: 'TSDeclareFunction',
460
+ loc: DUMMY_LOC,
461
+ id: {
462
+ type: 'Identifier',
463
+ loc: DUMMY_LOC,
464
+ name: 'isValid'
465
+ },
466
+ generator: false,
467
+ expression: false,
468
+ async: false,
469
+ params: [{
470
+ type: 'Identifier',
471
+ loc: DUMMY_LOC,
472
+ name: 'value',
473
+ typeAnnotation: {
474
+ type: 'TSTypeAnnotation',
475
+ loc: DUMMY_LOC,
476
+ typeAnnotation: {
477
+ type: 'TSUnionType',
478
+ loc: DUMMY_LOC,
479
+ types: [bodyRepresentationType, {
480
+ type: 'TSNullKeyword',
481
+ loc: DUMMY_LOC
482
+ }, {
483
+ type: 'TSUndefinedKeyword',
484
+ loc: DUMMY_LOC
485
+ }]
486
+ }
487
+ }
488
+ }],
489
+ returnType: {
490
+ type: 'TSTypeAnnotation',
491
+ loc: DUMMY_LOC,
492
+ typeAnnotation: {
493
+ type: 'TSTypePredicate',
494
+ loc: DUMMY_LOC,
495
+ asserts: false,
496
+ parameterName: {
497
+ type: 'Identifier',
498
+ loc: DUMMY_LOC,
499
+ name: 'value'
500
+ },
501
+ typeAnnotation: {
502
+ type: 'TSTypeAnnotation',
503
+ loc: DUMMY_LOC,
504
+ typeAnnotation: {
505
+ type: 'TSTypeReference',
506
+ loc: DUMMY_LOC,
507
+ typeName: enumName
508
+ }
509
+ }
510
+ }
511
+ }
512
+ },
513
+ specifiers: [],
514
+ source: null,
515
+ exportKind: 'value',
516
+ assertions: []
517
+ }, // export function members(): IterableIterator<Foo>;
518
+ {
519
+ type: 'ExportNamedDeclaration',
520
+ loc: DUMMY_LOC,
521
+ declaration: {
522
+ type: 'TSDeclareFunction',
523
+ loc: DUMMY_LOC,
524
+ id: {
525
+ type: 'Identifier',
526
+ loc: DUMMY_LOC,
527
+ name: 'members'
528
+ },
529
+ generator: false,
530
+ expression: false,
531
+ async: false,
532
+ params: [],
533
+ returnType: {
534
+ type: 'TSTypeAnnotation',
535
+ loc: DUMMY_LOC,
536
+ typeAnnotation: {
537
+ type: 'TSTypeReference',
538
+ loc: DUMMY_LOC,
539
+ typeName: {
540
+ type: 'Identifier',
541
+ loc: DUMMY_LOC,
542
+ name: 'IterableIterator'
543
+ },
544
+ typeParameters: {
545
+ type: 'TSTypeParameterInstantiation',
546
+ loc: DUMMY_LOC,
547
+ params: [{
548
+ type: 'TSTypeReference',
549
+ loc: DUMMY_LOC,
550
+ typeName: enumName
551
+ }]
552
+ }
553
+ }
554
+ }
555
+ },
556
+ specifiers: [],
557
+ source: null,
558
+ exportKind: 'value',
559
+ assertions: []
560
+ }, // export function getName(value: Foo): string;
561
+ {
562
+ type: 'ExportNamedDeclaration',
563
+ loc: DUMMY_LOC,
564
+ declaration: {
565
+ type: 'TSDeclareFunction',
566
+ loc: DUMMY_LOC,
567
+ id: {
568
+ type: 'Identifier',
569
+ loc: DUMMY_LOC,
570
+ name: 'getName'
571
+ },
572
+ generator: false,
573
+ expression: false,
574
+ async: false,
575
+ params: [{
576
+ type: 'Identifier',
577
+ loc: DUMMY_LOC,
578
+ name: 'value',
579
+ typeAnnotation: {
580
+ type: 'TSTypeAnnotation',
581
+ loc: DUMMY_LOC,
582
+ typeAnnotation: {
583
+ type: 'TSTypeReference',
584
+ loc: DUMMY_LOC,
585
+ typeName: enumName
586
+ }
587
+ }
588
+ }],
589
+ returnType: {
590
+ type: 'TSTypeAnnotation',
591
+ loc: DUMMY_LOC,
592
+ typeAnnotation: {
593
+ type: 'TSStringKeyword',
594
+ loc: DUMMY_LOC
595
+ }
596
+ }
597
+ },
598
+ specifiers: [],
599
+ source: null,
600
+ exportKind: 'value',
601
+ assertions: []
602
+ }]
603
+ }
604
+ }];
605
+ }
606
+
607
+ const getPlaceholderNameForTypeofImport = (() => {
608
+ let typeof_import_count = 0;
609
+ return () => `$$IMPORT_TYPEOF_${++typeof_import_count}$$`;
610
+ })();
611
+
612
+ const transformTypeAnnotationType = node => {
613
+ switch (node.type) {
614
+ case 'AnyTypeAnnotation':
615
+ return transform.AnyTypeAnnotation(node);
616
+
617
+ case 'ArrayTypeAnnotation':
618
+ return transform.ArrayTypeAnnotation(node);
619
+
620
+ case 'BigIntLiteralTypeAnnotation':
621
+ return transform.BigIntLiteralTypeAnnotation(node);
622
+
623
+ case 'BigIntTypeAnnotation':
624
+ return transform.BigIntTypeAnnotation(node);
625
+
626
+ case 'BooleanLiteralTypeAnnotation':
627
+ return transform.BooleanLiteralTypeAnnotation(node);
628
+
629
+ case 'BooleanTypeAnnotation':
630
+ return transform.BooleanTypeAnnotation(node);
631
+
632
+ case 'EmptyTypeAnnotation':
633
+ return transform.EmptyTypeAnnotation(node);
634
+
635
+ case 'ExistsTypeAnnotation':
636
+ return transform.ExistsTypeAnnotation(node);
637
+
638
+ case 'FunctionTypeAnnotation':
639
+ return transform.FunctionTypeAnnotation(node);
640
+
641
+ case 'GenericTypeAnnotation':
642
+ return transform.GenericTypeAnnotation(node);
643
+
644
+ case 'IndexedAccessType':
645
+ return transform.IndexedAccessType(node);
646
+
647
+ case 'InterfaceTypeAnnotation':
648
+ return transform.InterfaceTypeAnnotation(node);
649
+
650
+ case 'IntersectionTypeAnnotation':
651
+ return transform.IntersectionTypeAnnotation(node);
652
+
653
+ case 'MixedTypeAnnotation':
654
+ return transform.MixedTypeAnnotation(node);
655
+
656
+ case 'NullLiteralTypeAnnotation':
657
+ return transform.NullLiteralTypeAnnotation(node);
658
+
659
+ case 'NullableTypeAnnotation':
660
+ return transform.NullableTypeAnnotation(node);
661
+
662
+ case 'NumberLiteralTypeAnnotation':
663
+ return transform.NumberLiteralTypeAnnotation(node);
664
+
665
+ case 'NumberTypeAnnotation':
666
+ return transform.NumberTypeAnnotation(node);
667
+
668
+ case 'ObjectTypeAnnotation':
669
+ return transform.ObjectTypeAnnotation(node);
670
+
671
+ case 'OptionalIndexedAccessType':
672
+ return transform.OptionalIndexedAccessType(node);
673
+
674
+ case 'QualifiedTypeIdentifier':
675
+ return transform.QualifiedTypeIdentifier(node);
676
+
677
+ case 'StringLiteralTypeAnnotation':
678
+ return transform.StringLiteralTypeAnnotation(node);
679
+
680
+ case 'StringTypeAnnotation':
681
+ return transform.StringTypeAnnotation(node);
682
+
683
+ case 'SymbolTypeAnnotation':
684
+ return transform.SymbolTypeAnnotation(node);
685
+
686
+ case 'ThisTypeAnnotation':
687
+ return transform.ThisTypeAnnotation(node);
688
+
689
+ case 'TupleTypeAnnotation':
690
+ return transform.TupleTypeAnnotation(node);
691
+
692
+ case 'TypeofTypeAnnotation':
693
+ return transform.TypeofTypeAnnotation(node);
694
+
695
+ case 'UnionTypeAnnotation':
696
+ return transform.UnionTypeAnnotation(node);
697
+
698
+ case 'VoidTypeAnnotation':
699
+ return transform.VoidTypeAnnotation(node);
700
+
701
+ case 'TypePredicate':
702
+ return transform.TypePredicateAnnotation(node);
703
+
704
+ case 'ConditionalTypeAnnotation':
705
+ return transform.ConditionalTypeAnnotation(node);
706
+
707
+ case 'InferTypeAnnotation':
708
+ return transform.InferTypeAnnotation(node);
709
+
710
+ case 'KeyofTypeAnnotation':
711
+ return transform.KeyofTypeAnnotation(node);
712
+
713
+ case 'TypeOperator':
714
+ return transform.TypeOperator(node);
715
+
716
+ case 'ComponentTypeAnnotation':
717
+ return transform.ComponentTypeAnnotation(node);
718
+
719
+ default:
720
+ throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
721
+ }
722
+ };
723
+
724
+ const transform = {
725
+ AnyTypeAnnotation(_node) {
726
+ return {
727
+ type: 'TSAnyKeyword',
728
+ loc: DUMMY_LOC
729
+ };
730
+ },
731
+
732
+ ArrayTypeAnnotation(node) {
733
+ return {
734
+ type: 'TSArrayType',
735
+ loc: DUMMY_LOC,
736
+ elementType: transformTypeAnnotationType(node.elementType)
737
+ };
738
+ },
739
+
740
+ BigIntLiteral(node) {
741
+ return {
742
+ type: 'Literal',
743
+ loc: DUMMY_LOC,
744
+ bigint: node.bigint,
745
+ raw: node.raw,
746
+ value: node.value
747
+ };
748
+ },
749
+
750
+ BigIntLiteralTypeAnnotation(node) {
751
+ var _node$bigint;
752
+
753
+ // technically hermes doesn't support this yet
754
+ // but future proofing amirite
755
+ const bigint = // $FlowExpectedError[prop-missing]
756
+ (_node$bigint = node.bigint) != null ? _node$bigint : node.raw // estree spec is to not have a trailing `n` on this property
757
+ // https://github.com/estree/estree/blob/db962bb417a97effcfe9892f87fbb93c81a68584/es2020.md#bigintliteral
758
+ .replace(/n$/, '') // `BigInt` doesn't accept numeric separator and `bigint` property should not include numeric separator
759
+ .replace(/_/, '');
760
+ return {
761
+ type: 'TSLiteralType',
762
+ loc: DUMMY_LOC,
763
+ literal: {
764
+ type: 'Literal',
765
+ loc: DUMMY_LOC,
766
+ value: node.value,
767
+ raw: node.raw,
768
+ bigint
769
+ }
770
+ };
771
+ },
772
+
773
+ BigIntTypeAnnotation(_node) {
774
+ return {
775
+ type: 'TSBigIntKeyword',
776
+ loc: DUMMY_LOC
777
+ };
778
+ },
779
+
780
+ BooleanLiteral(node) {
781
+ return {
782
+ type: 'Literal',
783
+ loc: DUMMY_LOC,
784
+ raw: node.raw,
785
+ value: node.value
786
+ };
787
+ },
788
+
789
+ BooleanLiteralTypeAnnotation(node) {
790
+ return {
791
+ type: 'TSLiteralType',
792
+ loc: DUMMY_LOC,
793
+ literal: {
794
+ type: 'Literal',
795
+ loc: DUMMY_LOC,
796
+ value: node.value,
797
+ raw: node.raw
798
+ }
799
+ };
800
+ },
801
+
802
+ BooleanTypeAnnotation(_node) {
803
+ return {
804
+ type: 'TSBooleanKeyword',
805
+ loc: DUMMY_LOC
806
+ };
807
+ },
808
+
809
+ ClassImplements(node) {
810
+ return {
811
+ type: 'TSClassImplements',
812
+ loc: DUMMY_LOC,
813
+ expression: transform.Identifier(node.id, false),
814
+ typeParameters: transform.TypeParameterInstantiation(node.typeParameters)
815
+ };
816
+ },
817
+
818
+ DeclareClass(node) {
819
+ const classMembers = [];
820
+ const transformedBody = transform.ObjectTypeAnnotation(node.body);
821
+
822
+ if (transformedBody.type !== 'TSTypeLiteral') {
823
+ return unsupportedDeclaration(node.body, 'Spreads in declare class are not allowed', node.id, true, node.typeParameters);
824
+ }
825
+
826
+ for (const member of transformedBody.members) {
827
+ // TS uses the real ClassDeclaration AST so we need to
828
+ // make the signatures real methods/properties
829
+ switch (member.type) {
830
+ case 'TSIndexSignature':
831
+ {
832
+ classMembers.push(member);
833
+ break;
834
+ }
835
+
836
+ case 'TSMethodSignature':
837
+ {
838
+ // flow just creates a method signature like any other for a constructor
839
+ // but the proper AST has `kind = 'constructor'`
840
+ const isConstructor = (() => {
841
+ if (member.computed === true) {
842
+ return false;
843
+ }
844
+
845
+ return member.key.type === 'Identifier' && member.key.name === 'constructor' || member.key.type === 'Literal' && member.key.value === 'constructor';
846
+ })();
847
+
848
+ if (isConstructor) {
849
+ const newNode = {
850
+ type: 'MethodDefinition',
851
+ loc: DUMMY_LOC,
852
+ accessibility: undefined,
853
+ computed: false,
854
+ key: {
855
+ type: 'Identifier',
856
+ loc: DUMMY_LOC,
857
+ name: 'constructor'
858
+ },
859
+ kind: 'constructor',
860
+ optional: false,
861
+ override: false,
862
+ static: false,
863
+ value: {
864
+ type: 'TSEmptyBodyFunctionExpression',
865
+ loc: DUMMY_LOC,
866
+ async: false,
867
+ body: null,
868
+ declare: false,
869
+ expression: false,
870
+ generator: false,
871
+ id: null,
872
+ params: member.params,
873
+ // constructors explicitly have no return type
874
+ returnType: undefined,
875
+ typeParameters: member.typeParameters
876
+ }
877
+ };
878
+ cloneJSDocCommentsToNewNode(member, newNode);
879
+ classMembers.push(newNode);
880
+ } else {
881
+ var _member$static;
882
+
883
+ const [key, computed] = (() => {
884
+ const _key = member.key;
885
+
886
+ if (_key.type === 'Identifier' && _key.name.startsWith('@@')) {
887
+ const name = _key.name.slice(2);
888
+
889
+ if (['iterator', 'asyncIterator'].includes(name)) {
890
+ return [{
891
+ type: 'MemberExpression',
892
+ computed: false,
893
+ object: {
894
+ type: 'Identifier',
895
+ name: 'Symbol',
896
+ optional: false,
897
+ loc: DUMMY_LOC
898
+ },
899
+ optional: false,
900
+ property: {
901
+ type: 'Identifier',
902
+ name,
903
+ optional: false,
904
+ loc: DUMMY_LOC
905
+ },
906
+ loc: DUMMY_LOC
907
+ }, true];
908
+ }
909
+ }
910
+
911
+ return [member.key, member.computed];
912
+ })();
913
+
914
+ const newNode = {
915
+ type: 'MethodDefinition',
916
+ loc: DUMMY_LOC,
917
+ accessibility: member.accessibility,
918
+ computed: computed != null ? computed : false,
919
+ key,
920
+ kind: member.kind,
921
+ optional: member.optional,
922
+ override: false,
923
+ static: (_member$static = member.static) != null ? _member$static : false,
924
+ value: {
925
+ type: 'TSEmptyBodyFunctionExpression',
926
+ loc: DUMMY_LOC,
927
+ async: false,
928
+ body: null,
929
+ declare: false,
930
+ expression: false,
931
+ generator: false,
932
+ id: null,
933
+ params: member.params,
934
+ returnType: member.returnType,
935
+ typeParameters: member.typeParameters
936
+ }
937
+ };
938
+ cloneJSDocCommentsToNewNode(member, newNode);
939
+ classMembers.push(newNode);
940
+ }
941
+
942
+ break;
943
+ }
944
+
945
+ case 'TSPropertySignature':
946
+ {
947
+ var _member$computed, _member$static2;
948
+
949
+ const newNode = {
950
+ type: 'PropertyDefinition',
951
+ loc: DUMMY_LOC,
952
+ accessibility: member.accessibility,
953
+ computed: (_member$computed = member.computed) != null ? _member$computed : false,
954
+ declare: false,
955
+ key: member.key,
956
+ optional: member.optional,
957
+ readonly: member.readonly,
958
+ static: (_member$static2 = member.static) != null ? _member$static2 : false,
959
+ typeAnnotation: member.typeAnnotation,
960
+ value: null
961
+ };
962
+ cloneJSDocCommentsToNewNode(member, newNode);
963
+ classMembers.push(newNode);
964
+ break;
965
+ }
966
+
967
+ case 'TSCallSignatureDeclaration':
968
+ {
969
+ var _node$body$callProper;
970
+
971
+ /*
972
+ TODO - callProperties
973
+ It's not valid to directly declare a call property on a class in TS
974
+ You can do it, but it's a big complication in the AST:
975
+ ```ts
976
+ declare Class {
977
+ // ...
978
+ }
979
+ interface ClassConstructor {
980
+ new (): Class;
981
+ // call sigs
982
+ (): Type;
983
+ }
984
+ ```
985
+ Let's put a pin in it for now and deal with it later if the need arises.
986
+ */
987
+ return unsupportedDeclaration((_node$body$callProper = node.body.callProperties[0]) != null ? _node$body$callProper : node.body, 'call signatures on classes', node.id, true, node.typeParameters);
988
+ }
989
+
990
+ default:
991
+ throw unexpectedTranslationError(node.body, `Unexpected member type ${member.type}`);
992
+ }
993
+ }
994
+
995
+ const superClass = node.extends.length > 0 ? node.extends[0] : undefined;
996
+ return {
997
+ type: 'ClassDeclaration',
998
+ loc: DUMMY_LOC,
999
+ body: {
1000
+ type: 'ClassBody',
1001
+ loc: DUMMY_LOC,
1002
+ body: classMembers
1003
+ },
1004
+ declare: true,
1005
+ id: transform.Identifier(node.id, false),
1006
+ implements: node.implements == null ? undefined : node.implements.map(transform.ClassImplements),
1007
+ superClass: superClass == null ? null : superClass.id.type === 'QualifiedTypeIdentifier' ? transform.QualifiedTypeIdentifier(superClass.id) : transform.Identifier(superClass.id, false),
1008
+ superTypeParameters: transform.TypeParameterInstantiation(superClass == null ? void 0 : superClass.typeParameters),
1009
+ typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters) // TODO - mixins??
1010
+
1011
+ };
1012
+ },
1013
+
1014
+ DeclareExportDeclaration(node) {
1015
+ if (node.default === true) {
1016
+ const declaration = node.declaration;
1017
+
1018
+ switch (declaration.type) {
1019
+ // TS doesn't support direct default export for declare'd classes
1020
+ case 'DeclareClass':
1021
+ {
1022
+ const classDecl = transform.DeclareClass(declaration);
1023
+ const name = declaration.id.name;
1024
+ return [classDecl, {
1025
+ type: 'ExportDefaultDeclaration',
1026
+ loc: DUMMY_LOC,
1027
+ declaration: {
1028
+ type: 'Identifier',
1029
+ loc: DUMMY_LOC,
1030
+ name
1031
+ },
1032
+ exportKind: 'value'
1033
+ }];
1034
+ }
1035
+ // TS doesn't support direct default export for declare'd functions
1036
+
1037
+ case 'DeclareFunction':
1038
+ {
1039
+ const functionDecl = transform.DeclareFunction(declaration);
1040
+ const name = declaration.id.name;
1041
+ return [functionDecl, {
1042
+ type: 'ExportDefaultDeclaration',
1043
+ loc: DUMMY_LOC,
1044
+ declaration: {
1045
+ type: 'Identifier',
1046
+ loc: DUMMY_LOC,
1047
+ name
1048
+ },
1049
+ exportKind: 'value'
1050
+ }];
1051
+ }
1052
+ // TS doesn't support direct default export for declare'd functions
1053
+
1054
+ case 'DeclareComponent':
1055
+ {
1056
+ const functionDecl = transform.DeclareComponent(declaration);
1057
+ const name = declaration.id.name;
1058
+ return [functionDecl, {
1059
+ type: 'ExportDefaultDeclaration',
1060
+ loc: DUMMY_LOC,
1061
+ declaration: {
1062
+ type: 'Identifier',
1063
+ loc: DUMMY_LOC,
1064
+ name
1065
+ },
1066
+ exportKind: 'value'
1067
+ }];
1068
+ }
1069
+ // TS doesn't support direct default export for declare'd functions
1070
+
1071
+ case 'DeclareHook':
1072
+ {
1073
+ const functionDecl = transform.DeclareHook(declaration);
1074
+ const name = declaration.id.name;
1075
+ return [functionDecl, {
1076
+ type: 'ExportDefaultDeclaration',
1077
+ loc: DUMMY_LOC,
1078
+ declaration: {
1079
+ type: 'Identifier',
1080
+ loc: DUMMY_LOC,
1081
+ name
1082
+ },
1083
+ exportKind: 'value'
1084
+ }];
1085
+ }
1086
+ // Flow's declare export default Identifier is ambiguous.
1087
+ // the Identifier might reference a type, or it might reference a value
1088
+ // - If it's a value, then that's all good, TS supports that.
1089
+ // - If it's a type, that's a problem - TS only allows value variables to be exported
1090
+ // so we need to create an intermediate variable to hold the type.
1091
+
1092
+ case 'GenericTypeAnnotation':
1093
+ {
1094
+ const referencedId = declaration.id; // QualifiedTypeIdentifiers are types so cannot be handled without the intermediate variable so
1095
+ // only Identifiers can be handled here.
1096
+
1097
+ if (referencedId.type === 'Identifier') {
1098
+ const exportedVar = topScope.set.get(referencedId.name);
1099
+
1100
+ if (exportedVar != null && exportedVar.defs.length === 1) {
1101
+ const def = exportedVar.defs[0];
1102
+
1103
+ switch (def.type) {
1104
+ case 'ImportBinding':
1105
+ {
1106
+ // `import type { Wut } from 'mod'; declare export default Wut;`
1107
+ // `import { type Wut } from 'mod'; declare export default Wut;`
1108
+ // these cases should be wrapped in a variable because they're exporting a type, not a value
1109
+ const specifier = def.node;
1110
+
1111
+ if (specifier.importKind === 'type' || specifier.parent.importKind === 'type') {
1112
+ // fallthrough to the "default" handling
1113
+ break;
1114
+ } // intentional fallthrough to the "value" handling
1115
+
1116
+ }
1117
+
1118
+ case 'ClassName':
1119
+ case 'Enum':
1120
+ case 'FunctionName':
1121
+ case 'ImplicitGlobalVariable':
1122
+ case 'Variable':
1123
+ // there's already a variable defined to hold the type
1124
+ return {
1125
+ type: 'ExportDefaultDeclaration',
1126
+ loc: DUMMY_LOC,
1127
+ declaration: {
1128
+ type: 'Identifier',
1129
+ loc: DUMMY_LOC,
1130
+ name: referencedId.name
1131
+ },
1132
+ exportKind: 'value'
1133
+ };
1134
+
1135
+ case 'CatchClause':
1136
+ case 'Parameter':
1137
+ case 'TypeParameter':
1138
+ throw translationError(def.node, `Unexpected variable def type: ${def.type}`);
1139
+
1140
+ case 'Type':
1141
+ // fallthrough to the "default" handling
1142
+ break;
1143
+ }
1144
+ }
1145
+ } // intentional fallthrough to the "default" handling
1146
+
1147
+ }
1148
+
1149
+ case 'TypeofTypeAnnotation':
1150
+ {
1151
+ if (declaration.type === 'TypeofTypeAnnotation' && declaration.argument.type === 'Identifier') {
1152
+ const name = declaration.argument.name;
1153
+ const exportedVar = topScope.set.get(name);
1154
+
1155
+ if (exportedVar != null && exportedVar.defs.length === 1) {
1156
+ const def = exportedVar.defs[0];
1157
+
1158
+ switch (def.type) {
1159
+ case 'ClassName':
1160
+ {
1161
+ return {
1162
+ type: 'ExportDefaultDeclaration',
1163
+ declaration: {
1164
+ type: 'Identifier',
1165
+ decorators: [],
1166
+ name,
1167
+ optional: false,
1168
+ loc: DUMMY_LOC
1169
+ },
1170
+ exportKind: 'value',
1171
+ loc: DUMMY_LOC
1172
+ };
1173
+ }
1174
+ }
1175
+ }
1176
+ } // intentional fallthrough to the "default" handling
1177
+
1178
+ }
1179
+
1180
+ default:
1181
+ {
1182
+ /*
1183
+ flow allows syntax like
1184
+ ```
1185
+ declare export default TypeName;
1186
+ ```
1187
+ but TS does not, so we have to declare a temporary variable to
1188
+ reference in the export declaration:
1189
+ ```
1190
+ declare const $$EXPORT_DEFAULT_DECLARATION$$: TypeName;
1191
+ export default $$EXPORT_DEFAULT_DECLARATION$$;
1192
+ ```
1193
+ */
1194
+ const SPECIFIER = '$$EXPORT_DEFAULT_DECLARATION$$';
1195
+ return [{
1196
+ type: 'VariableDeclaration',
1197
+ loc: DUMMY_LOC,
1198
+ declarations: [{
1199
+ type: 'VariableDeclarator',
1200
+ loc: DUMMY_LOC,
1201
+ id: {
1202
+ type: 'Identifier',
1203
+ loc: DUMMY_LOC,
1204
+ name: SPECIFIER,
1205
+ typeAnnotation: {
1206
+ type: 'TSTypeAnnotation',
1207
+ loc: DUMMY_LOC,
1208
+ typeAnnotation: transformTypeAnnotationType(declaration)
1209
+ }
1210
+ },
1211
+ init: null
1212
+ }],
1213
+ declare: true,
1214
+ kind: 'const'
1215
+ }, {
1216
+ type: 'TSTypeAliasDeclaration',
1217
+ declare: true,
1218
+ id: {
1219
+ type: 'Identifier',
1220
+ decorators: [],
1221
+ name: SPECIFIER,
1222
+ optional: false,
1223
+ loc: DUMMY_LOC
1224
+ },
1225
+ typeAnnotation: {
1226
+ type: 'TSTypeQuery',
1227
+ exprName: {
1228
+ type: 'Identifier',
1229
+ decorators: [],
1230
+ name: SPECIFIER,
1231
+ optional: false,
1232
+ loc: DUMMY_LOC
1233
+ },
1234
+ loc: DUMMY_LOC
1235
+ },
1236
+ loc: DUMMY_LOC
1237
+ }, {
1238
+ type: 'ExportDefaultDeclaration',
1239
+ loc: DUMMY_LOC,
1240
+ declaration: {
1241
+ type: 'Identifier',
1242
+ loc: DUMMY_LOC,
1243
+ name: SPECIFIER
1244
+ },
1245
+ exportKind: 'value'
1246
+ }];
1247
+ }
1248
+ }
1249
+ } else {
1250
+ // eslint-disable-next-line eqeqeq
1251
+ if (node.source === null) {
1252
+ // eslint-disable-next-line eqeqeq
1253
+ if (node.declaration === null) {
1254
+ return {
1255
+ type: 'ExportNamedDeclaration',
1256
+ loc: DUMMY_LOC,
1257
+ // flow does not currently support assertions
1258
+ assertions: [],
1259
+ declaration: null,
1260
+ // flow does not support declared type exports with specifiers
1261
+ exportKind: 'value',
1262
+ source: null,
1263
+ specifiers: node.specifiers.map(transform.ExportSpecifier)
1264
+ };
1265
+ }
1266
+
1267
+ const declarations = (() => {
1268
+ switch (node.declaration.type) {
1269
+ case 'DeclareClass':
1270
+ return [{
1271
+ declaration: transform.DeclareClass(node.declaration),
1272
+ exportKind: 'value'
1273
+ }];
1274
+
1275
+ case 'DeclareComponent':
1276
+ return [{
1277
+ declaration: transform.DeclareComponent(node.declaration),
1278
+ exportKind: 'value'
1279
+ }];
1280
+
1281
+ case 'DeclareHook':
1282
+ return [{
1283
+ declaration: transform.DeclareHook(node.declaration),
1284
+ exportKind: 'value'
1285
+ }];
1286
+
1287
+ case 'DeclareFunction':
1288
+ return [{
1289
+ declaration: transform.DeclareFunction(node.declaration),
1290
+ exportKind: 'value'
1291
+ }];
1292
+
1293
+ case 'DeclareInterface':
1294
+ return [{
1295
+ declaration: transform.DeclareInterface(node.declaration),
1296
+ exportKind: 'type'
1297
+ }];
1298
+
1299
+ case 'DeclareOpaqueType':
1300
+ return [{
1301
+ declaration: transform.DeclareOpaqueType(node.declaration),
1302
+ exportKind: 'type'
1303
+ }];
1304
+
1305
+ case 'DeclareVariable':
1306
+ return [{
1307
+ declaration: transform.DeclareVariable(node.declaration),
1308
+ exportKind: 'value'
1309
+ }];
1310
+
1311
+ case 'DeclareEnum':
1312
+ {
1313
+ const result = transform.DeclareEnum(node.declaration);
1314
+ return Array.isArray(result) ? [{
1315
+ declaration: result[0],
1316
+ exportKind: 'type'
1317
+ }, {
1318
+ declaration: result[1],
1319
+ exportKind: 'type'
1320
+ }] : [{
1321
+ declaration: result,
1322
+ exportKind: 'type'
1323
+ }];
1324
+ }
1325
+ }
1326
+ })();
1327
+
1328
+ const mappedDeclarations = declarations.map(({
1329
+ declaration,
1330
+ exportKind
1331
+ }) => {
1332
+ if (declaration.type === 'VariableDeclaration' && declaration.declarations.length === 1) {
1333
+ const ident = declaration.declarations[0].id;
1334
+
1335
+ if (ident.type === 'Identifier') {
1336
+ const name = ident.name;
1337
+ return [{
1338
+ type: 'ExportNamedDeclaration',
1339
+ loc: DUMMY_LOC,
1340
+ // flow does not currently support assertions
1341
+ assertions: [],
1342
+ declaration,
1343
+ exportKind,
1344
+ source: null,
1345
+ specifiers: []
1346
+ }, {
1347
+ type: 'ExportNamedDeclaration',
1348
+ declaration: {
1349
+ type: 'TSTypeAliasDeclaration',
1350
+ declare: true,
1351
+ id: {
1352
+ type: 'Identifier',
1353
+ decorators: [],
1354
+ name,
1355
+ optional: false,
1356
+ loc: DUMMY_LOC
1357
+ },
1358
+ typeAnnotation: {
1359
+ type: 'TSTypeQuery',
1360
+ exprName: {
1361
+ type: 'Identifier',
1362
+ decorators: [],
1363
+ name,
1364
+ optional: false,
1365
+ loc: DUMMY_LOC
1366
+ },
1367
+ loc: DUMMY_LOC
1368
+ },
1369
+ loc: DUMMY_LOC
1370
+ },
1371
+ source: null,
1372
+ loc: DUMMY_LOC,
1373
+ specifiers: [],
1374
+ exportKind: 'type',
1375
+ // flow does not currently support assertions
1376
+ assertions: []
1377
+ }];
1378
+ }
1379
+ }
1380
+
1381
+ const exportNamedDeclaration = {
1382
+ type: 'ExportNamedDeclaration',
1383
+ loc: DUMMY_LOC,
1384
+ // flow does not currently support assertions
1385
+ assertions: [],
1386
+ declaration,
1387
+ exportKind,
1388
+ source: null,
1389
+ specifiers: []
1390
+ };
1391
+ return exportNamedDeclaration;
1392
+ });
1393
+ return mappedDeclarations.flat();
1394
+ } else {
1395
+ return {
1396
+ type: 'ExportNamedDeclaration',
1397
+ loc: DUMMY_LOC,
1398
+ // flow does not currently support assertions
1399
+ assertions: [],
1400
+ declaration: null,
1401
+ // flow does not support declared type exports with a source
1402
+ exportKind: 'value',
1403
+ source: transform.StringLiteral(node.source),
1404
+ specifiers: node.specifiers.map(transform.ExportSpecifier)
1405
+ };
1406
+ }
1407
+ }
1408
+ },
1409
+
1410
+ DeclareComponent(node) {
1411
+ const id = transform.Identifier(node.id, false);
1412
+ const typeParameters = node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters);
1413
+ const params = transform.ComponentTypeParameters(node.params, node.rest); // TS cannot support `renderType` so we always use ReactNode as the return type.
1414
+
1415
+ const hasReactImport = isReactImport(node, 'React');
1416
+ const returnType = {
1417
+ type: 'TSTypeAnnotation',
1418
+ loc: DUMMY_LOC,
1419
+ // If no rendersType we assume its ReactNode type.
1420
+ typeAnnotation: {
1421
+ type: 'TSTypeReference',
1422
+ loc: DUMMY_LOC,
1423
+ typeName: {
1424
+ type: 'TSQualifiedName',
1425
+ loc: DUMMY_LOC,
1426
+ left: getReactIdentifier(hasReactImport),
1427
+ right: {
1428
+ type: 'Identifier',
1429
+ loc: DUMMY_LOC,
1430
+ name: `ReactNode`
1431
+ }
1432
+ },
1433
+ typeParameters: undefined
1434
+ }
1435
+ };
1436
+ return {
1437
+ type: 'TSDeclareFunction',
1438
+ loc: DUMMY_LOC,
1439
+ async: false,
1440
+ body: undefined,
1441
+ declare: true,
1442
+ expression: false,
1443
+ generator: false,
1444
+ id: {
1445
+ type: 'Identifier',
1446
+ loc: DUMMY_LOC,
1447
+ name: id.name
1448
+ },
1449
+ params,
1450
+ returnType: returnType,
1451
+ typeParameters: typeParameters
1452
+ };
1453
+ },
1454
+
1455
+ ComponentTypeParameters(params, rest) {
1456
+ if (params.length === 0 && rest != null) {
1457
+ return [{
1458
+ type: 'Identifier',
1459
+ loc: DUMMY_LOC,
1460
+ name: 'props',
1461
+ typeAnnotation: {
1462
+ type: 'TSTypeAnnotation',
1463
+ loc: DUMMY_LOC,
1464
+ typeAnnotation: transformTypeAnnotationType(rest.typeAnnotation)
1465
+ },
1466
+ optional: false
1467
+ }];
1468
+ }
1469
+
1470
+ const flowPropsType = [];
1471
+
1472
+ if (rest != null) {
1473
+ flowPropsType.push(constructFlowNode({
1474
+ type: 'ObjectTypeSpreadProperty',
1475
+ argument: rest.typeAnnotation,
1476
+ range: rest.range,
1477
+ loc: rest.loc
1478
+ }));
1479
+ }
1480
+
1481
+ for (let i = 0; i < params.length; i++) {
1482
+ var _param$name;
1483
+
1484
+ const param = params[i];
1485
+ flowPropsType.push(constructFlowNode({
1486
+ type: 'ObjectTypeProperty',
1487
+ kind: 'init',
1488
+ method: false,
1489
+ optional: param.optional,
1490
+ variance: null,
1491
+ proto: false,
1492
+ static: false,
1493
+ key: (_param$name = param.name) != null ? _param$name : constructFlowNode({
1494
+ type: 'Identifier',
1495
+ name: `$$PARAM_${i}$$`,
1496
+ optional: false,
1497
+ typeAnnotation: null
1498
+ }),
1499
+ value: param.typeAnnotation,
1500
+ range: param.range,
1501
+ loc: param.loc
1502
+ }));
1503
+ }
1504
+
1505
+ const tsPropsObjectType = transform.ObjectTypeAnnotation(constructFlowNode({
1506
+ type: 'ObjectTypeAnnotation',
1507
+ inexact: false,
1508
+ exact: true,
1509
+ properties: flowPropsType,
1510
+ indexers: [],
1511
+ callProperties: [],
1512
+ internalSlots: []
1513
+ }));
1514
+ return [{
1515
+ type: 'Identifier',
1516
+ loc: DUMMY_LOC,
1517
+ name: 'props',
1518
+ typeAnnotation: {
1519
+ type: 'TSTypeAnnotation',
1520
+ loc: DUMMY_LOC,
1521
+ typeAnnotation: tsPropsObjectType
1522
+ },
1523
+ optional: false
1524
+ }];
1525
+ },
1526
+
1527
+ DeclareHook(node) {
1528
+ // the hook params/returnType are stored as an annotation on the ID...
1529
+ const id = transform.Identifier(node.id, false);
1530
+ const functionInfo = transform.FunctionTypeAnnotation(node.id.typeAnnotation.typeAnnotation);
1531
+ return {
1532
+ type: 'TSDeclareFunction',
1533
+ loc: DUMMY_LOC,
1534
+ async: false,
1535
+ body: undefined,
1536
+ declare: true,
1537
+ expression: false,
1538
+ generator: false,
1539
+ id: {
1540
+ type: 'Identifier',
1541
+ loc: DUMMY_LOC,
1542
+ name: id.name
1543
+ },
1544
+ params: functionInfo.params,
1545
+ returnType: functionInfo.returnType,
1546
+ typeParameters: functionInfo.typeParameters
1547
+ };
1548
+ },
1549
+
1550
+ DeclareFunction(node) {
1551
+ // the function information is stored as an annotation on the ID...
1552
+ const id = transform.Identifier(node.id, false);
1553
+ const functionInfo = transform.FunctionTypeAnnotation(node.id.typeAnnotation.typeAnnotation);
1554
+ return {
1555
+ type: 'TSDeclareFunction',
1556
+ loc: DUMMY_LOC,
1557
+ async: false,
1558
+ body: undefined,
1559
+ declare: true,
1560
+ expression: false,
1561
+ generator: false,
1562
+ id: {
1563
+ type: 'Identifier',
1564
+ loc: DUMMY_LOC,
1565
+ name: id.name
1566
+ },
1567
+ params: functionInfo.params,
1568
+ returnType: functionInfo.returnType,
1569
+ typeParameters: functionInfo.typeParameters
1570
+ };
1571
+ },
1572
+
1573
+ DeclareInterface(node) {
1574
+ const transformedBody = transform.ObjectTypeAnnotation(node.body);
1575
+
1576
+ if (transformedBody.type !== 'TSTypeLiteral') {
1577
+ throw translationError(node.body, 'Spreads in interfaces are not allowed');
1578
+ }
1579
+
1580
+ return {
1581
+ type: 'TSInterfaceDeclaration',
1582
+ loc: DUMMY_LOC,
1583
+ body: {
1584
+ type: 'TSInterfaceBody',
1585
+ loc: DUMMY_LOC,
1586
+ body: transformedBody.members
1587
+ },
1588
+ declare: node.type !== 'InterfaceDeclaration',
1589
+ extends: node.extends.map(transform.InterfaceExtends),
1590
+ id: transform.Identifier(node.id, false),
1591
+ typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
1592
+ };
1593
+ },
1594
+
1595
+ DeclareTypeAlias(node) {
1596
+ return {
1597
+ type: 'TSTypeAliasDeclaration',
1598
+ loc: DUMMY_LOC,
1599
+ declare: node.type === 'DeclareTypeAlias',
1600
+ id: transform.Identifier(node.id, false),
1601
+ typeAnnotation: transformTypeAnnotationType(node.right),
1602
+ typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
1603
+ };
1604
+ },
1605
+
1606
+ DeclareOpaqueType(node) {
1607
+ // TS doesn't currently have nominal types - https://github.com/Microsoft/Typescript/issues/202
1608
+ // TODO - we could simulate this in a variety of ways
1609
+ // Examples - https://basarat.gitbook.io/typescript/main-1/nominaltyping
1610
+ if (node.supertype == null && node.typeParameters == null) {
1611
+ const name = `__${node.id.name}__`;
1612
+ return {
1613
+ type: 'TSTypeAliasDeclaration',
1614
+ loc: DUMMY_LOC,
1615
+ declare: true,
1616
+ id: transform.Identifier(node.id, false),
1617
+ typeAnnotation: {
1618
+ type: 'TSIntersectionType',
1619
+ types: [{
1620
+ type: 'TSSymbolKeyword',
1621
+ loc: DUMMY_LOC
1622
+ }, {
1623
+ type: 'TSTypeLiteral',
1624
+ loc: DUMMY_LOC,
1625
+ members: [{
1626
+ type: 'TSPropertySignature',
1627
+ computed: false,
1628
+ loc: DUMMY_LOC,
1629
+ key: {
1630
+ type: 'Identifier',
1631
+ name: name,
1632
+ loc: DUMMY_LOC
1633
+ },
1634
+ typeAnnotation: {
1635
+ type: 'TSTypeAnnotation',
1636
+ loc: DUMMY_LOC,
1637
+ typeAnnotation: {
1638
+ type: 'TSStringKeyword',
1639
+ loc: DUMMY_LOC
1640
+ }
1641
+ }
1642
+ }]
1643
+ }],
1644
+ loc: DUMMY_LOC
1645
+ }
1646
+ };
1647
+ }
1648
+
1649
+ return {
1650
+ type: 'TSTypeAliasDeclaration',
1651
+ loc: DUMMY_LOC,
1652
+ declare: true,
1653
+ id: transform.Identifier(node.id, false),
1654
+ typeAnnotation: node.supertype == null ? {
1655
+ type: 'TSUnknownKeyword',
1656
+ loc: DUMMY_LOC
1657
+ } : transformTypeAnnotationType(node.supertype),
1658
+ typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
1659
+ };
1660
+ },
1661
+
1662
+ DeclareVariable(node) {
1663
+ return {
1664
+ type: 'VariableDeclaration',
1665
+ loc: DUMMY_LOC,
1666
+ declare: true,
1667
+ declarations: [{
1668
+ type: 'VariableDeclarator',
1669
+ loc: DUMMY_LOC,
1670
+ declare: true,
1671
+ id: transform.Identifier(node.id, true),
1672
+ init: null
1673
+ }],
1674
+ kind: node.kind
1675
+ };
1676
+ },
1677
+
1678
+ DeclareEnum(node) {
1679
+ return EnumImpl(node);
1680
+ },
1681
+
1682
+ EmptyTypeAnnotation(node) {
1683
+ // Flow's `empty` type doesn't map well to any types in TS.
1684
+ // The closest is `never`, but `never` has a number of different semantics
1685
+ // In reality no human code should ever directly use the `empty` type in flow
1686
+ // So let's put a pin in it for now
1687
+ return unsupportedAnnotation(node, 'empty type');
1688
+ },
1689
+
1690
+ EnumDeclaration(node) {
1691
+ return EnumImpl(node);
1692
+ },
1693
+
1694
+ DeclareModuleExports(node) {
1695
+ throw translationError(node, 'CommonJS exports are not supported.');
1696
+ },
1697
+
1698
+ ExistsTypeAnnotation(node) {
1699
+ // The existential type does not map to any types in TS
1700
+ // It's also super deprecated - so let's not ever worry
1701
+ return unsupportedAnnotation(node, 'existential type');
1702
+ },
1703
+
1704
+ ExportAllDeclaration(node) {
1705
+ return {
1706
+ type: 'ExportAllDeclaration',
1707
+ loc: DUMMY_LOC,
1708
+ // flow does not currently support import/export assertions
1709
+ assertions: [],
1710
+ exportKind: node.exportKind,
1711
+ source: transform.StringLiteral(node.source),
1712
+ exported: node.exported == null ? null : transform.Identifier(node.exported)
1713
+ };
1714
+ },
1715
+
1716
+ ExportNamedDeclaration(node) {
1717
+ if (node.source != null || node.specifiers.length > 0) {
1718
+ // can never have a declaration with a source
1719
+ return {
1720
+ type: 'ExportNamedDeclaration',
1721
+ loc: DUMMY_LOC,
1722
+ // flow does not currently support import/export assertions
1723
+ assertions: [],
1724
+ declaration: null,
1725
+ exportKind: node.exportKind,
1726
+ source: node.source == null ? null : transform.StringLiteral(node.source),
1727
+ specifiers: node.specifiers.map(transform.ExportSpecifier)
1728
+ };
1729
+ }
1730
+
1731
+ const [exportedDeclaration, mergedDeclaration] = (() => {
1732
+ if (node.declaration == null) {
1733
+ return [null, null];
1734
+ }
1735
+
1736
+ switch (node.declaration.type) {
1737
+ case 'ClassDeclaration':
1738
+ case 'ComponentDeclaration':
1739
+ case 'HookDeclaration':
1740
+ case 'FunctionDeclaration':
1741
+ case 'VariableDeclaration':
1742
+ // These cases shouldn't happen in flow defs because they have their own special
1743
+ // AST node (DeclareClass, DeclareFunction, DeclareVariable)
1744
+ throw unexpectedTranslationError(node.declaration, `Unexpected named declaration found ${node.declaration.type}`);
1745
+
1746
+ case 'EnumDeclaration':
1747
+ {
1748
+ const result = transform.EnumDeclaration(node.declaration);
1749
+ return Array.isArray(result) ? result : [result, null];
1750
+ }
1751
+
1752
+ case 'InterfaceDeclaration':
1753
+ return [transform.InterfaceDeclaration(node.declaration), null];
1754
+
1755
+ case 'OpaqueType':
1756
+ return [transform.OpaqueType(node.declaration), null];
1757
+
1758
+ case 'TypeAlias':
1759
+ return [transform.TypeAlias(node.declaration), null];
1760
+ }
1761
+ })();
1762
+
1763
+ const mainExport = {
1764
+ type: 'ExportNamedDeclaration',
1765
+ loc: DUMMY_LOC,
1766
+ assertions: [],
1767
+ declaration: exportedDeclaration,
1768
+ exportKind: node.exportKind,
1769
+ source: null,
1770
+ specifiers: []
1771
+ };
1772
+
1773
+ if (mergedDeclaration != null) {
1774
+ // for cases where there is a merged declaration, TS enforces BOTH are exported
1775
+ return [mainExport, {
1776
+ type: 'ExportNamedDeclaration',
1777
+ loc: DUMMY_LOC,
1778
+ assertions: [],
1779
+ declaration: mergedDeclaration,
1780
+ exportKind: node.exportKind,
1781
+ source: null,
1782
+ specifiers: []
1783
+ }];
1784
+ }
1785
+
1786
+ return mainExport;
1787
+ },
1788
+
1789
+ ExportSpecifier(node) {
1790
+ return {
1791
+ type: 'ExportSpecifier',
1792
+ loc: DUMMY_LOC,
1793
+ exported: transform.Identifier(node.exported, false),
1794
+ local: transform.Identifier(node.local, false),
1795
+ // flow does not support inline exportKind for named exports
1796
+ exportKind: 'value'
1797
+ };
1798
+ },
1799
+
1800
+ FunctionTypeAnnotation(node) {
1801
+ const params = node.params.map(transform.FunctionTypeParam);
1802
+
1803
+ if (node.type === 'FunctionTypeAnnotation' && node.this != null) {
1804
+ params.unshift({
1805
+ type: 'Identifier',
1806
+ loc: DUMMY_LOC,
1807
+ name: 'this',
1808
+ typeAnnotation: {
1809
+ type: 'TSTypeAnnotation',
1810
+ loc: DUMMY_LOC,
1811
+ typeAnnotation: transformTypeAnnotationType(node.this.typeAnnotation)
1812
+ }
1813
+ });
1814
+ }
1815
+
1816
+ if (node.rest != null) {
1817
+ const rest = node.rest;
1818
+ params.push({
1819
+ type: 'RestElement',
1820
+ loc: DUMMY_LOC,
1821
+ argument: rest.name == null ? {
1822
+ type: 'Identifier',
1823
+ loc: DUMMY_LOC,
1824
+ name: '$$REST$$'
1825
+ } : transform.Identifier(rest.name, false),
1826
+ typeAnnotation: {
1827
+ type: 'TSTypeAnnotation',
1828
+ loc: DUMMY_LOC,
1829
+ typeAnnotation: transformTypeAnnotationType(rest.typeAnnotation)
1830
+ }
1831
+ });
1832
+ }
1833
+
1834
+ return {
1835
+ type: 'TSFunctionType',
1836
+ loc: DUMMY_LOC,
1837
+ params,
1838
+ returnType: {
1839
+ type: 'TSTypeAnnotation',
1840
+ loc: DUMMY_LOC,
1841
+ typeAnnotation: transformTypeAnnotationType(node.returnType)
1842
+ },
1843
+ typeParameters: node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters)
1844
+ };
1845
+ },
1846
+
1847
+ FunctionTypeParam(node, idx = 0) {
1848
+ return {
1849
+ type: 'Identifier',
1850
+ loc: DUMMY_LOC,
1851
+ name: node.name == null ? `$$PARAM_${idx}$$` : node.name.name,
1852
+ typeAnnotation: {
1853
+ type: 'TSTypeAnnotation',
1854
+ loc: DUMMY_LOC,
1855
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation)
1856
+ },
1857
+ optional: node.optional
1858
+ };
1859
+ },
1860
+
1861
+ GenericTypeAnnotation(node) {
1862
+ const [fullTypeName, baseId] = (() => {
1863
+ let names = [];
1864
+ let currentNode = node.id;
1865
+
1866
+ while (currentNode != null) {
1867
+ switch (currentNode.type) {
1868
+ case 'Identifier':
1869
+ {
1870
+ names.unshift(currentNode.name);
1871
+ return [names.join('.'), currentNode];
1872
+ }
1873
+
1874
+ case 'QualifiedTypeIdentifier':
1875
+ {
1876
+ names.unshift(currentNode.id.name);
1877
+ currentNode = currentNode.qualification;
1878
+ break;
1879
+ }
1880
+ }
1881
+ }
1882
+
1883
+ throw translationError(node, `Invalid program state, types should only contain 'Identifier' and 'QualifiedTypeIdentifier' nodes.`);
1884
+ })();
1885
+
1886
+ const assertHasExactlyNTypeParameters = count => {
1887
+ if (node.typeParameters != null) {
1888
+ if (node.typeParameters.params.length !== count) {
1889
+ throw translationError(node, `Expected exactly ${count} type parameter${count > 1 ? 's' : ''} with \`${fullTypeName}\``);
1890
+ }
1891
+
1892
+ const res = [];
1893
+
1894
+ for (const param of node.typeParameters.params) {
1895
+ res.push(transformTypeAnnotationType(param));
1896
+ }
1897
+
1898
+ return res;
1899
+ }
1900
+
1901
+ if (count !== 0) {
1902
+ throw translationError(node, `Expected no type parameters with \`${fullTypeName}\``);
1903
+ }
1904
+
1905
+ return [];
1906
+ };
1907
+
1908
+ function assertHasTypeParametersInRange(min, max) {
1909
+ const {
1910
+ typeParameters
1911
+ } = node;
1912
+
1913
+ if (typeParameters == null) {
1914
+ if (min > 0) {
1915
+ throw translationError(node, `Expected between ${min} and ${max} type parameters with \`${fullTypeName}\`, but got none.`);
1916
+ }
1917
+
1918
+ return [];
1919
+ }
1920
+
1921
+ const params = typeParameters.params;
1922
+
1923
+ if (params.length < min || params.length > max) {
1924
+ throw translationError(node, `Expected between ${min} and ${max} type parameters with \`${fullTypeName}\`, but got ${params.length}.`);
1925
+ }
1926
+
1927
+ return typeParameters.params.map(transformTypeAnnotationType);
1928
+ }
1929
+
1930
+ switch (fullTypeName) {
1931
+ case '$Call':
1932
+ case '$ObjMap':
1933
+ case '$ObjMapConst':
1934
+ case '$ObjMapi':
1935
+ case '$TupleMap':
1936
+ {
1937
+ /*
1938
+ TODO - I don't think it's possible to make these types work in the generic case.
1939
+ TS has no utility types that allow you to generically mimic this functionality.
1940
+ You really need intimiate knowledge of the user's intent in order to correctly
1941
+ transform the code.
1942
+ For example the simple example for $Call from the flow docs:
1943
+ ```
1944
+ type ExtractPropType = <T>({prop: T}) => T;
1945
+ type Obj = {prop: number};
1946
+ type PropType = $Call<ExtractPropType, Obj>;
1947
+ // expected -- typeof PropType === number
1948
+ ```
1949
+ The equivalent in TS would be:
1950
+ ```
1951
+ type ExtractPropType<T extends { prop: any }> = (arg: T) => T['prop'];
1952
+ type Obj = { prop: number };
1953
+ type PropType = ReturnType<ExtractPropType<Obj>>; // number
1954
+ ```
1955
+ */
1956
+ return unsupportedAnnotation(node, fullTypeName);
1957
+ }
1958
+
1959
+ case '$ArrayBufferView':
1960
+ {
1961
+ // `$ArrayBufferView` => `ArrayBufferView`
1962
+ return {
1963
+ type: 'TSTypeReference',
1964
+ loc: DUMMY_LOC,
1965
+ typeName: {
1966
+ type: 'Identifier',
1967
+ loc: DUMMY_LOC,
1968
+ name: 'ArrayBufferView'
1969
+ }
1970
+ };
1971
+ }
1972
+
1973
+ case '$ArrayLike':
1974
+ {
1975
+ // `$ArrayLike<T>` => `ArrayLike<T>`
1976
+ return {
1977
+ type: 'TSTypeReference',
1978
+ loc: DUMMY_LOC,
1979
+ typeName: {
1980
+ type: 'Identifier',
1981
+ loc: DUMMY_LOC,
1982
+ name: 'ArrayLike'
1983
+ },
1984
+ typeParameters: {
1985
+ type: 'TSTypeParameterInstantiation',
1986
+ loc: DUMMY_LOC,
1987
+ params: assertHasExactlyNTypeParameters(1)
1988
+ }
1989
+ };
1990
+ }
1991
+
1992
+ case '$Diff':
1993
+ case '$Rest':
1994
+ {
1995
+ // `$Diff<A, B>` => `Pick<A, Exclude<keyof A, keyof B>>`
1996
+ const params = assertHasExactlyNTypeParameters(2);
1997
+ return {
1998
+ type: 'TSTypeReference',
1999
+ loc: DUMMY_LOC,
2000
+ typeName: {
2001
+ type: 'Identifier',
2002
+ loc: DUMMY_LOC,
2003
+ name: 'Pick'
2004
+ },
2005
+ typeParameters: {
2006
+ type: 'TSTypeParameterInstantiation',
2007
+ loc: DUMMY_LOC,
2008
+ params: [params[0], {
2009
+ type: 'TSTypeReference',
2010
+ loc: DUMMY_LOC,
2011
+ typeName: {
2012
+ type: 'Identifier',
2013
+ loc: DUMMY_LOC,
2014
+ name: 'Exclude'
2015
+ },
2016
+ typeParameters: {
2017
+ type: 'TSTypeParameterInstantiation',
2018
+ loc: DUMMY_LOC,
2019
+ params: [{
2020
+ type: 'TSTypeOperator',
2021
+ loc: DUMMY_LOC,
2022
+ operator: 'keyof',
2023
+ typeAnnotation: params[0]
2024
+ }, {
2025
+ type: 'TSTypeOperator',
2026
+ loc: DUMMY_LOC,
2027
+ operator: 'keyof',
2028
+ typeAnnotation: params[1]
2029
+ }]
2030
+ }
2031
+ }]
2032
+ }
2033
+ };
2034
+ }
2035
+
2036
+ case '$ElementType':
2037
+ case '$PropertyType':
2038
+ {
2039
+ // `$ElementType<T, K>` => `T[K]`
2040
+ const params = assertHasExactlyNTypeParameters(2);
2041
+ return {
2042
+ type: 'TSIndexedAccessType',
2043
+ loc: DUMMY_LOC,
2044
+ objectType: params[0],
2045
+ indexType: params[1]
2046
+ };
2047
+ }
2048
+
2049
+ case '$Exact':
2050
+ {
2051
+ // `$Exact<T>` => `T`
2052
+ // TS has no concept of exact vs inexact types
2053
+ return assertHasExactlyNTypeParameters(1)[0];
2054
+ }
2055
+
2056
+ case '$Exports':
2057
+ {
2058
+ // `$Exports<'module'>` => `typeof import('module')`
2059
+ const moduleName = assertHasExactlyNTypeParameters(1)[0];
2060
+
2061
+ if (moduleName.type !== 'TSLiteralType' || moduleName.literal.type !== 'Literal' || typeof moduleName.literal.value !== 'string') {
2062
+ throw translationError(node, '$Exports must have a string literal argument');
2063
+ }
2064
+
2065
+ return {
2066
+ type: 'TSImportType',
2067
+ loc: DUMMY_LOC,
2068
+ isTypeOf: true,
2069
+ argument: moduleName,
2070
+ qualifier: null,
2071
+ typeParameters: null
2072
+ };
2073
+ }
2074
+
2075
+ case '$FlowFixMe':
2076
+ {
2077
+ // `$FlowFixMe` => `any`
2078
+ return {
2079
+ type: 'TSAnyKeyword',
2080
+ loc: DUMMY_LOC
2081
+ };
2082
+ }
2083
+
2084
+ case '$KeyMirror':
2085
+ {
2086
+ // `$KeyMirror<T>` => `{[K in keyof T]: K}`
2087
+ return {
2088
+ type: 'TSMappedType',
2089
+ loc: DUMMY_LOC,
2090
+ typeParameter: {
2091
+ type: 'TSTypeParameter',
2092
+ loc: DUMMY_LOC,
2093
+ name: {
2094
+ type: 'Identifier',
2095
+ loc: DUMMY_LOC,
2096
+ name: 'K'
2097
+ },
2098
+ constraint: {
2099
+ type: 'TSTypeOperator',
2100
+ loc: DUMMY_LOC,
2101
+ operator: 'keyof',
2102
+ typeAnnotation: assertHasExactlyNTypeParameters(1)[0]
2103
+ },
2104
+ in: false,
2105
+ out: false
2106
+ },
2107
+ nameType: null,
2108
+ typeAnnotation: {
2109
+ type: 'TSTypeReference',
2110
+ loc: DUMMY_LOC,
2111
+ typeName: {
2112
+ type: 'Identifier',
2113
+ loc: DUMMY_LOC,
2114
+ name: 'K'
2115
+ }
2116
+ }
2117
+ };
2118
+ }
2119
+
2120
+ case '$Keys':
2121
+ {
2122
+ // `$Keys<T>` => `keyof T`
2123
+ return {
2124
+ type: 'TSTypeOperator',
2125
+ loc: DUMMY_LOC,
2126
+ operator: 'keyof',
2127
+ typeAnnotation: assertHasExactlyNTypeParameters(1)[0]
2128
+ };
2129
+ }
2130
+
2131
+ case '$NonMaybeType':
2132
+ {
2133
+ // `$NonMaybeType<T>` => `NonNullable<T>`
2134
+ // Not a great name because `NonNullable` also excludes `undefined`
2135
+ return {
2136
+ type: 'TSTypeReference',
2137
+ loc: DUMMY_LOC,
2138
+ typeName: {
2139
+ type: 'Identifier',
2140
+ loc: DUMMY_LOC,
2141
+ name: 'NonNullable'
2142
+ },
2143
+ typeParameters: {
2144
+ type: 'TSTypeParameterInstantiation',
2145
+ loc: DUMMY_LOC,
2146
+ params: assertHasExactlyNTypeParameters(1)
2147
+ }
2148
+ };
2149
+ }
2150
+
2151
+ case '$ReadOnly':
2152
+ {
2153
+ // `$ReadOnly<T>` => `Readonly<T>`
2154
+ return {
2155
+ type: 'TSTypeReference',
2156
+ loc: DUMMY_LOC,
2157
+ typeName: {
2158
+ type: 'Identifier',
2159
+ loc: DUMMY_LOC,
2160
+ name: 'Readonly'
2161
+ },
2162
+ typeParameters: {
2163
+ type: 'TSTypeParameterInstantiation',
2164
+ loc: DUMMY_LOC,
2165
+ params: assertHasExactlyNTypeParameters(1)
2166
+ }
2167
+ };
2168
+ }
2169
+
2170
+ case '$ReadOnlyArray':
2171
+ {
2172
+ // `$ReadOnlyArray<T>` => `ReadonlyArray<T>`
2173
+ //
2174
+ // we could also do => `readonly T[]`
2175
+ // TODO - maybe a config option?
2176
+ return {
2177
+ type: 'TSTypeReference',
2178
+ loc: DUMMY_LOC,
2179
+ typeName: {
2180
+ type: 'Identifier',
2181
+ loc: DUMMY_LOC,
2182
+ name: 'ReadonlyArray'
2183
+ },
2184
+ typeParameters: {
2185
+ type: 'TSTypeParameterInstantiation',
2186
+ loc: DUMMY_LOC,
2187
+ params: assertHasExactlyNTypeParameters(1)
2188
+ }
2189
+ };
2190
+ }
2191
+
2192
+ case '$ReadOnlyMap':
2193
+ {
2194
+ return {
2195
+ type: 'TSTypeReference',
2196
+ loc: DUMMY_LOC,
2197
+ typeName: {
2198
+ type: 'Identifier',
2199
+ loc: DUMMY_LOC,
2200
+ name: 'ReadonlyMap'
2201
+ },
2202
+ typeParameters: {
2203
+ type: 'TSTypeParameterInstantiation',
2204
+ loc: DUMMY_LOC,
2205
+ params: assertHasExactlyNTypeParameters(2)
2206
+ }
2207
+ };
2208
+ }
2209
+
2210
+ case '$ReadOnlySet':
2211
+ {
2212
+ return {
2213
+ type: 'TSTypeReference',
2214
+ loc: DUMMY_LOC,
2215
+ typeName: {
2216
+ type: 'Identifier',
2217
+ loc: DUMMY_LOC,
2218
+ name: 'ReadonlySet'
2219
+ },
2220
+ typeParameters: {
2221
+ type: 'TSTypeParameterInstantiation',
2222
+ loc: DUMMY_LOC,
2223
+ params: assertHasExactlyNTypeParameters(1)
2224
+ }
2225
+ };
2226
+ }
2227
+
2228
+ case '$Values':
2229
+ {
2230
+ // `$Values<T>` => `T[keyof T]`
2231
+ const transformedType = assertHasExactlyNTypeParameters(1)[0];
2232
+ return {
2233
+ type: 'TSIndexedAccessType',
2234
+ loc: DUMMY_LOC,
2235
+ objectType: transformedType,
2236
+ indexType: {
2237
+ type: 'TSTypeOperator',
2238
+ loc: DUMMY_LOC,
2239
+ operator: 'keyof',
2240
+ typeAnnotation: transformedType
2241
+ }
2242
+ };
2243
+ }
2244
+
2245
+ case 'Class':
2246
+ {
2247
+ // `Class<T>` => `new (...args: any[]) => T`
2248
+ const param = assertHasExactlyNTypeParameters(1)[0];
2249
+
2250
+ if (param.type !== 'TSTypeReference') {
2251
+ throw translationError(node, 'Expected a type reference within Class<T>');
2252
+ }
2253
+
2254
+ return {
2255
+ type: 'TSConstructorType',
2256
+ loc: DUMMY_LOC,
2257
+ abstract: false,
2258
+ params: [{
2259
+ type: 'RestElement',
2260
+ loc: DUMMY_LOC,
2261
+ argument: {
2262
+ type: 'Identifier',
2263
+ loc: DUMMY_LOC,
2264
+ name: 'args'
2265
+ },
2266
+ typeAnnotation: {
2267
+ type: 'TSTypeAnnotation',
2268
+ loc: DUMMY_LOC,
2269
+ typeAnnotation: {
2270
+ type: 'TSArrayType',
2271
+ loc: DUMMY_LOC,
2272
+ elementType: {
2273
+ type: 'TSAnyKeyword',
2274
+ loc: DUMMY_LOC
2275
+ }
2276
+ }
2277
+ }
2278
+ }],
2279
+ returnType: {
2280
+ type: 'TSTypeAnnotation',
2281
+ loc: DUMMY_LOC,
2282
+ typeAnnotation: param
2283
+ }
2284
+ };
2285
+ }
2286
+
2287
+ case 'StringPrefix':
2288
+ {
2289
+ var _params$;
2290
+
2291
+ // `StringPrefix<foo>` => `foo${string}`
2292
+ // `StringPrefix<foo, T>` => `foo${T}`
2293
+ const params = assertHasTypeParametersInRange(1, 2);
2294
+ const prefix = params[0];
2295
+
2296
+ if (prefix.type !== 'TSLiteralType' || typeof prefix.literal.value !== 'string') {
2297
+ throw translationError(node, 'Expected a string literal for the first type parameter.');
2298
+ }
2299
+
2300
+ const prefixStr = prefix.literal.value;
2301
+ const remainder = (_params$ = params[1]) != null ? _params$ : {
2302
+ type: 'TSStringKeyword',
2303
+ loc: DUMMY_LOC
2304
+ };
2305
+ return {
2306
+ type: 'TSTemplateLiteralType',
2307
+ loc: DUMMY_LOC,
2308
+ quasis: [{
2309
+ type: 'TemplateElement',
2310
+ loc: DUMMY_LOC,
2311
+ value: {
2312
+ raw: prefixStr,
2313
+ cooked: prefixStr
2314
+ },
2315
+ tail: false
2316
+ }, {
2317
+ type: 'TemplateElement',
2318
+ loc: DUMMY_LOC,
2319
+ value: {
2320
+ raw: '',
2321
+ cooked: ''
2322
+ },
2323
+ tail: true
2324
+ }],
2325
+ types: [remainder]
2326
+ };
2327
+ }
2328
+
2329
+ case 'StringSuffix':
2330
+ {
2331
+ var _params$2;
2332
+
2333
+ // `StringSuffix<foo>` => `${string}foo`
2334
+ // `StringSuffix<foo, T>` => `${T}foo`
2335
+ const params = assertHasTypeParametersInRange(1, 2);
2336
+ const suffix = params[0];
2337
+
2338
+ if (suffix.type !== 'TSLiteralType' || typeof suffix.literal.value !== 'string') {
2339
+ throw translationError(node, 'Expected a string literal for the first type parameter.');
2340
+ }
2341
+
2342
+ const suffixStr = suffix.literal.value;
2343
+ const remainder = (_params$2 = params[1]) != null ? _params$2 : {
2344
+ type: 'TSStringKeyword',
2345
+ loc: DUMMY_LOC
2346
+ };
2347
+ return {
2348
+ type: 'TSTemplateLiteralType',
2349
+ loc: DUMMY_LOC,
2350
+ quasis: [{
2351
+ type: 'TemplateElement',
2352
+ loc: DUMMY_LOC,
2353
+ value: {
2354
+ raw: '',
2355
+ cooked: ''
2356
+ },
2357
+ tail: false
2358
+ }, {
2359
+ type: 'TemplateElement',
2360
+ loc: DUMMY_LOC,
2361
+ value: {
2362
+ raw: suffixStr,
2363
+ cooked: suffixStr
2364
+ },
2365
+ tail: true
2366
+ }],
2367
+ types: [remainder]
2368
+ };
2369
+ }
2370
+ } // React special conversion:
2371
+
2372
+
2373
+ const validReactImportOrGlobal = isValidReactImportOrGlobal(baseId);
2374
+ const hasReactImport = isReactImport(baseId, baseId.name);
2375
+
2376
+ if (validReactImportOrGlobal || hasReactImport) {
2377
+ switch (fullTypeName) {
2378
+ // TODO: In flow this is `ChildrenArray<T> = T | $ReadOnlyArray<ChildrenArray<T>>`.
2379
+ // The recursive nature of it is rarely needed, so we're simplifying this for now
2380
+ // but omitting that aspect. Once we're able to provide utility types for our translations,
2381
+ // we should update this.
2382
+ // React.ChildrenArray<T> -> T | ReadonlyArray<T>
2383
+ // React$ChildrenArray<T> -> T | ReadonlyArray<T>
2384
+ case 'React.ChildrenArray':
2385
+ case 'React$ChildrenArray':
2386
+ {
2387
+ const [param] = assertHasExactlyNTypeParameters(1);
2388
+ return {
2389
+ type: 'TSUnionType',
2390
+ loc: DUMMY_LOC,
2391
+ types: [param, {
2392
+ type: 'TSTypeReference',
2393
+ loc: DUMMY_LOC,
2394
+ typeName: {
2395
+ type: 'Identifier',
2396
+ loc: DUMMY_LOC,
2397
+ name: 'ReadonlyArray'
2398
+ },
2399
+ typeParameters: {
2400
+ type: 'TSTypeParameterInstantiation',
2401
+ loc: DUMMY_LOC,
2402
+ params: [param]
2403
+ }
2404
+ }]
2405
+ };
2406
+ }
2407
+ // React.Component<A,B> -> React.Component<A,B>
2408
+ // React$Component<A,B> -> React.Component<A,B>
2409
+
2410
+ case 'React.Component':
2411
+ case 'React$Component':
2412
+ {
2413
+ const typeParameters = node.typeParameters;
2414
+
2415
+ if (typeParameters == null || typeParameters.params.length === 0) {
2416
+ throw translationError(node, `Expected at least 1 type parameter with \`${fullTypeName}\``);
2417
+ }
2418
+
2419
+ const params = typeParameters.params;
2420
+
2421
+ if (params.length > 2) {
2422
+ throw translationError(node, `Expected at no more than 2 type parameters with \`${fullTypeName}\``);
2423
+ }
2424
+
2425
+ return {
2426
+ type: 'TSTypeReference',
2427
+ loc: DUMMY_LOC,
2428
+ typeName: {
2429
+ type: 'TSQualifiedName',
2430
+ loc: DUMMY_LOC,
2431
+ left: getReactIdentifier(hasReactImport),
2432
+ right: {
2433
+ type: 'Identifier',
2434
+ loc: DUMMY_LOC,
2435
+ name: 'Component'
2436
+ }
2437
+ },
2438
+ typeParameters: {
2439
+ type: 'TSTypeParameterInstantiation',
2440
+ loc: DUMMY_LOC,
2441
+ params: params.map(param => transformTypeAnnotationType(param))
2442
+ }
2443
+ };
2444
+ }
2445
+ // React.Context<A> -> React.Context<A>
2446
+ // React$Context<A> -> React.Context<A>
2447
+
2448
+ case 'React$Context':
2449
+ case 'React.Context':
2450
+ return {
2451
+ type: 'TSTypeReference',
2452
+ loc: DUMMY_LOC,
2453
+ typeName: {
2454
+ type: 'TSQualifiedName',
2455
+ loc: DUMMY_LOC,
2456
+ left: getReactIdentifier(hasReactImport),
2457
+ right: {
2458
+ type: 'Identifier',
2459
+ loc: DUMMY_LOC,
2460
+ name: `Context`
2461
+ }
2462
+ },
2463
+ typeParameters: {
2464
+ type: 'TSTypeParameterInstantiation',
2465
+ loc: DUMMY_LOC,
2466
+ params: assertHasExactlyNTypeParameters(1)
2467
+ }
2468
+ };
2469
+ // React.Key -> React.Key
2470
+ // React$Key -> React.Key
2471
+
2472
+ case 'React.Key':
2473
+ case 'React$Key':
2474
+ assertHasExactlyNTypeParameters(0);
2475
+ return {
2476
+ type: 'TSTypeReference',
2477
+ loc: DUMMY_LOC,
2478
+ typeName: {
2479
+ type: 'TSQualifiedName',
2480
+ loc: DUMMY_LOC,
2481
+ left: getReactIdentifier(hasReactImport),
2482
+ right: {
2483
+ type: 'Identifier',
2484
+ loc: DUMMY_LOC,
2485
+ name: 'Key'
2486
+ }
2487
+ }
2488
+ };
2489
+ // React.ElementType -> React.ElementType
2490
+ // React$ElementType -> React.ElementType
2491
+
2492
+ case 'React$ElementType':
2493
+ case 'React.ElementType':
2494
+ {
2495
+ assertHasExactlyNTypeParameters(0);
2496
+ return {
2497
+ type: 'TSTypeReference',
2498
+ loc: DUMMY_LOC,
2499
+ typeName: {
2500
+ type: 'TSQualifiedName',
2501
+ loc: DUMMY_LOC,
2502
+ left: getReactIdentifier(hasReactImport),
2503
+ right: {
2504
+ type: 'Identifier',
2505
+ loc: DUMMY_LOC,
2506
+ name: `ElementType`
2507
+ }
2508
+ },
2509
+ typeParameters: undefined
2510
+ };
2511
+ }
2512
+ // React.Node -> React.ReactNode
2513
+
2514
+ case 'React$Node':
2515
+ case 'React.Node':
2516
+ {
2517
+ assertHasExactlyNTypeParameters(0);
2518
+ return {
2519
+ type: 'TSTypeReference',
2520
+ loc: DUMMY_LOC,
2521
+ typeName: {
2522
+ type: 'TSQualifiedName',
2523
+ loc: DUMMY_LOC,
2524
+ left: getReactIdentifier(hasReactImport),
2525
+ right: {
2526
+ type: 'Identifier',
2527
+ loc: DUMMY_LOC,
2528
+ name: `ReactNode`
2529
+ }
2530
+ },
2531
+ typeParameters: undefined
2532
+ };
2533
+ }
2534
+ // React.Element<typeof Component> -> React.ReactElement<typeof Component>
2535
+
2536
+ case 'React$Element':
2537
+ case 'React.Element':
2538
+ {
2539
+ return {
2540
+ type: 'TSTypeReference',
2541
+ loc: DUMMY_LOC,
2542
+ typeName: {
2543
+ type: 'TSQualifiedName',
2544
+ loc: DUMMY_LOC,
2545
+ left: getReactIdentifier(hasReactImport),
2546
+ right: {
2547
+ type: 'Identifier',
2548
+ loc: DUMMY_LOC,
2549
+ name: `ReactElement`
2550
+ }
2551
+ },
2552
+ typeParameters: {
2553
+ type: 'TSTypeParameterInstantiation',
2554
+ loc: DUMMY_LOC,
2555
+ params: assertHasExactlyNTypeParameters(1)
2556
+ }
2557
+ };
2558
+ }
2559
+ // React.ElementRef<typeof Component> -> React.ComponentRef<typeof Component>
2560
+ // React$ElementRef<typeof Component> -> React.ComponentRef<typeof Component>
2561
+
2562
+ case 'React$ElementRef':
2563
+ case 'React.ElementRef':
2564
+ return {
2565
+ type: 'TSTypeReference',
2566
+ loc: DUMMY_LOC,
2567
+ typeName: {
2568
+ type: 'TSQualifiedName',
2569
+ loc: DUMMY_LOC,
2570
+ left: getReactIdentifier(hasReactImport),
2571
+ right: {
2572
+ type: 'Identifier',
2573
+ loc: DUMMY_LOC,
2574
+ name: `ComponentRef`
2575
+ }
2576
+ },
2577
+ typeParameters: {
2578
+ type: 'TSTypeParameterInstantiation',
2579
+ loc: DUMMY_LOC,
2580
+ params: assertHasExactlyNTypeParameters(1)
2581
+ }
2582
+ };
2583
+ // React$Fragment -> React.Fragment
2584
+ // React.Fragment -> React.Fragment
2585
+
2586
+ case 'React$FragmentType':
2587
+ case 'React.Fragment':
2588
+ assertHasExactlyNTypeParameters(0);
2589
+ return {
2590
+ type: 'TSTypeReference',
2591
+ loc: DUMMY_LOC,
2592
+ typeName: {
2593
+ type: 'TSQualifiedName',
2594
+ loc: DUMMY_LOC,
2595
+ left: getReactIdentifier(hasReactImport),
2596
+ right: {
2597
+ type: 'Identifier',
2598
+ loc: DUMMY_LOC,
2599
+ name: `Fragment`
2600
+ }
2601
+ }
2602
+ };
2603
+ // React.MixedElement -> React.JSX.Element
2604
+
2605
+ case 'React$MixedElement':
2606
+ case 'React.MixedElement':
2607
+ {
2608
+ assertHasExactlyNTypeParameters(0);
2609
+ return {
2610
+ type: 'TSTypeReference',
2611
+ loc: DUMMY_LOC,
2612
+ typeName: {
2613
+ type: 'TSQualifiedName',
2614
+ loc: DUMMY_LOC,
2615
+ left: {
2616
+ type: 'TSQualifiedName',
2617
+ loc: DUMMY_LOC,
2618
+ left: {
2619
+ type: 'Identifier',
2620
+ loc: DUMMY_LOC,
2621
+ name: 'React'
2622
+ },
2623
+ right: {
2624
+ type: 'Identifier',
2625
+ loc: DUMMY_LOC,
2626
+ name: 'JSX'
2627
+ }
2628
+ },
2629
+ right: {
2630
+ type: 'Identifier',
2631
+ loc: DUMMY_LOC,
2632
+ name: 'Element'
2633
+ }
2634
+ },
2635
+ typeParameters: undefined
2636
+ };
2637
+ }
2638
+ // React.ComponentType<Config> -> React.ComponentType<Config>
2639
+ // React$ComponentType<Config> -> React.ComponentType<Config>
2640
+
2641
+ case 'React.ComponentType':
2642
+ case 'React$ComponentType':
2643
+ {
2644
+ return {
2645
+ type: 'TSTypeReference',
2646
+ loc: DUMMY_LOC,
2647
+ typeName: {
2648
+ type: 'TSQualifiedName',
2649
+ loc: DUMMY_LOC,
2650
+ left: getReactIdentifier(hasReactImport),
2651
+ right: {
2652
+ type: 'Identifier',
2653
+ loc: DUMMY_LOC,
2654
+ name: 'ComponentType'
2655
+ }
2656
+ },
2657
+ typeParameters: {
2658
+ type: 'TSTypeParameterInstantiation',
2659
+ loc: DUMMY_LOC,
2660
+ params: assertHasExactlyNTypeParameters(1)
2661
+ }
2662
+ };
2663
+ }
2664
+ // React.AbstractComponent<Config> -> React.ComponentType<Config>
2665
+ // React$AbstractComponent<Config> -> React.ComponentType<Config>
2666
+ // React.AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
2667
+ // React$AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
2668
+
2669
+ case 'React.AbstractComponent':
2670
+ case 'React$AbstractComponent':
2671
+ {
2672
+ const typeParameters = node.typeParameters;
2673
+
2674
+ if (typeParameters == null || typeParameters.params.length === 0) {
2675
+ throw translationError(node, `Expected at least 1 type parameter with \`${fullTypeName}\``);
2676
+ }
2677
+
2678
+ const params = typeParameters.params;
2679
+
2680
+ if (params.length > 3) {
2681
+ throw translationError(node, `Expected at no more than 3 type parameters with \`${fullTypeName}\``);
2682
+ }
2683
+
2684
+ const newParams = (() => {
2685
+ if (params.length === 1) {
2686
+ return assertHasExactlyNTypeParameters(1);
2687
+ }
2688
+
2689
+ const props = transformTypeAnnotationType(params[0]);
2690
+ const ref = transformTypeAnnotationType(params[1]);
2691
+ return [{
2692
+ type: 'TSIntersectionType',
2693
+ loc: DUMMY_LOC,
2694
+ types: [props, {
2695
+ type: 'TSTypeReference',
2696
+ loc: DUMMY_LOC,
2697
+ typeName: {
2698
+ type: 'TSQualifiedName',
2699
+ loc: DUMMY_LOC,
2700
+ left: {
2701
+ type: 'Identifier',
2702
+ loc: DUMMY_LOC,
2703
+ name: 'React'
2704
+ },
2705
+ right: {
2706
+ type: 'Identifier',
2707
+ loc: DUMMY_LOC,
2708
+ name: 'RefAttributes'
2709
+ }
2710
+ },
2711
+ typeParameters: {
2712
+ type: 'TSTypeParameterInstantiation',
2713
+ loc: DUMMY_LOC,
2714
+ params: [ref]
2715
+ }
2716
+ }]
2717
+ }];
2718
+ })();
2719
+
2720
+ return {
2721
+ type: 'TSTypeReference',
2722
+ loc: DUMMY_LOC,
2723
+ typeName: {
2724
+ type: 'TSQualifiedName',
2725
+ loc: DUMMY_LOC,
2726
+ left: getReactIdentifier(hasReactImport),
2727
+ right: {
2728
+ type: 'Identifier',
2729
+ loc: DUMMY_LOC,
2730
+ name: 'ComponentType'
2731
+ }
2732
+ },
2733
+ typeParameters: {
2734
+ type: 'TSTypeParameterInstantiation',
2735
+ loc: DUMMY_LOC,
2736
+ params: newParams
2737
+ }
2738
+ };
2739
+ }
2740
+ // React.ElementProps<A> -> React.ComponentProps<A>
2741
+ // React$ElementProps<A> -> React.ComponentProps<A>
2742
+
2743
+ case 'React.ElementProps':
2744
+ case 'React$ElementProps':
2745
+ {
2746
+ return {
2747
+ type: 'TSTypeReference',
2748
+ loc: DUMMY_LOC,
2749
+ typeName: {
2750
+ type: 'TSQualifiedName',
2751
+ loc: DUMMY_LOC,
2752
+ left: getReactIdentifier(hasReactImport),
2753
+ right: {
2754
+ type: 'Identifier',
2755
+ loc: DUMMY_LOC,
2756
+ name: 'ComponentProps'
2757
+ }
2758
+ },
2759
+ typeParameters: {
2760
+ type: 'TSTypeParameterInstantiation',
2761
+ loc: DUMMY_LOC,
2762
+ params: assertHasExactlyNTypeParameters(1)
2763
+ }
2764
+ };
2765
+ }
2766
+ // React.ElementConfig<A> -> React.JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
2767
+ // React$ElementConfig<A> -> React.JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
2768
+
2769
+ case 'React.ElementConfig':
2770
+ case 'React$ElementConfig':
2771
+ {
2772
+ const [param] = assertHasExactlyNTypeParameters(1);
2773
+ return {
2774
+ type: 'TSTypeReference',
2775
+ loc: DUMMY_LOC,
2776
+ typeName: {
2777
+ type: 'TSQualifiedName',
2778
+ loc: DUMMY_LOC,
2779
+ left: {
2780
+ type: 'TSQualifiedName',
2781
+ loc: DUMMY_LOC,
2782
+ left: {
2783
+ type: 'Identifier',
2784
+ loc: DUMMY_LOC,
2785
+ name: 'React'
2786
+ },
2787
+ right: {
2788
+ type: 'Identifier',
2789
+ loc: DUMMY_LOC,
2790
+ name: 'JSX'
2791
+ }
2792
+ },
2793
+ right: {
2794
+ type: 'Identifier',
2795
+ loc: DUMMY_LOC,
2796
+ name: 'LibraryManagedAttributes'
2797
+ }
2798
+ },
2799
+ typeParameters: {
2800
+ type: 'TSTypeParameterInstantiation',
2801
+ loc: DUMMY_LOC,
2802
+ params: [param, {
2803
+ type: 'TSTypeReference',
2804
+ loc: DUMMY_LOC,
2805
+ typeName: {
2806
+ type: 'TSQualifiedName',
2807
+ loc: DUMMY_LOC,
2808
+ left: getReactIdentifier(hasReactImport),
2809
+ right: {
2810
+ type: 'Identifier',
2811
+ loc: DUMMY_LOC,
2812
+ name: `ComponentProps`
2813
+ }
2814
+ },
2815
+ typeParameters: {
2816
+ type: 'TSTypeParameterInstantiation',
2817
+ loc: DUMMY_LOC,
2818
+ params: [param]
2819
+ }
2820
+ }]
2821
+ }
2822
+ };
2823
+ }
2824
+ // React.RefSetter<C> -> React.Ref<C>
2825
+ // React$RefSetter<C> -> React.Ref<C>
2826
+
2827
+ case 'React.RefSetter':
2828
+ case 'React$RefSetter':
2829
+ return {
2830
+ type: 'TSTypeReference',
2831
+ loc: DUMMY_LOC,
2832
+ typeName: {
2833
+ type: 'TSQualifiedName',
2834
+ loc: DUMMY_LOC,
2835
+ left: getReactIdentifier(hasReactImport),
2836
+ right: {
2837
+ type: 'Identifier',
2838
+ loc: DUMMY_LOC,
2839
+ name: 'Ref'
2840
+ }
2841
+ },
2842
+ typeParameters: {
2843
+ type: 'TSTypeParameterInstantiation',
2844
+ loc: DUMMY_LOC,
2845
+ params: assertHasExactlyNTypeParameters(1)
2846
+ }
2847
+ };
2848
+
2849
+ default:
2850
+ return unsupportedAnnotation(node, fullTypeName);
2851
+ }
2852
+ }
2853
+
2854
+ return {
2855
+ type: 'TSTypeReference',
2856
+ loc: DUMMY_LOC,
2857
+ typeName: node.id.type === 'Identifier' ? transform.Identifier(node.id, false) : transform.QualifiedTypeIdentifier(node.id),
2858
+ typeParameters: transform.TypeParameterInstantiation(node.typeParameters)
2859
+ };
2860
+ },
2861
+
2862
+ Identifier(node, includeTypeAnnotation = true) {
2863
+ return {
2864
+ type: 'Identifier',
2865
+ loc: DUMMY_LOC,
2866
+ name: node.name,
2867
+ ...(includeTypeAnnotation && node.typeAnnotation != null ? {
2868
+ typeAnnotation: transform.TypeAnnotation(node.typeAnnotation)
2869
+ } : {})
2870
+ };
2871
+ },
2872
+
2873
+ IndexedAccessType(node) {
2874
+ return {
2875
+ type: 'TSIndexedAccessType',
2876
+ loc: DUMMY_LOC,
2877
+ objectType: transformTypeAnnotationType(node.objectType),
2878
+ indexType: transformTypeAnnotationType(node.indexType)
2879
+ };
2880
+ },
2881
+
2882
+ InterfaceDeclaration(node) {
2883
+ return transform.DeclareInterface(node);
2884
+ },
2885
+
2886
+ ImportAttribute(node) {
2887
+ return {
2888
+ type: 'ImportAttribute',
2889
+ loc: DUMMY_LOC,
2890
+ key: node.key.type === 'Identifier' ? transform.Identifier(node.key) : transform.Literal(node.key),
2891
+ value: transform.Literal(node.value)
2892
+ };
2893
+ },
2894
+
2895
+ ImportDeclaration(node) {
2896
+ const importKind = node.importKind;
2897
+ const specifiers = [];
2898
+ const unsupportedSpecifiers = [];
2899
+ node.specifiers.forEach(spec => {
2900
+ let id = (() => {
2901
+ if (node.importKind === 'typeof' || spec.importKind === 'typeof') {
2902
+ const id = {
2903
+ type: 'Identifier',
2904
+ loc: DUMMY_LOC,
2905
+ name: getPlaceholderNameForTypeofImport()
2906
+ };
2907
+ unsupportedSpecifiers.push({
2908
+ type: 'TSTypeAliasDeclaration',
2909
+ loc: DUMMY_LOC,
2910
+ id: transform.Identifier(spec.local, false),
2911
+ typeAnnotation: {
2912
+ type: 'TSTypeQuery',
2913
+ loc: DUMMY_LOC,
2914
+ exprName: id
2915
+ }
2916
+ });
2917
+ return id;
2918
+ }
2919
+
2920
+ return transform.Identifier(spec.local, false);
2921
+ })();
2922
+
2923
+ switch (spec.type) {
2924
+ case 'ImportDefaultSpecifier':
2925
+ specifiers.push({
2926
+ type: 'ImportDefaultSpecifier',
2927
+ loc: DUMMY_LOC,
2928
+ local: id
2929
+ });
2930
+ return;
2931
+
2932
+ case 'ImportNamespaceSpecifier':
2933
+ specifiers.push({
2934
+ type: 'ImportNamespaceSpecifier',
2935
+ loc: DUMMY_LOC,
2936
+ local: id
2937
+ });
2938
+ return;
2939
+
2940
+ case 'ImportSpecifier':
2941
+ specifiers.push({
2942
+ type: 'ImportSpecifier',
2943
+ loc: DUMMY_LOC,
2944
+ importKind: spec.importKind === 'typeof' || spec.importKind === 'type' ? 'type' : null,
2945
+ imported: transform.Identifier(spec.imported, false),
2946
+ local: id
2947
+ });
2948
+ return;
2949
+ }
2950
+ });
2951
+ const out = specifiers.length ? [{
2952
+ type: 'ImportDeclaration',
2953
+ loc: DUMMY_LOC,
2954
+ assertions: node.assertions.map(transform.ImportAttribute),
2955
+ importKind: importKind === 'typeof' ? 'type' : importKind != null ? importKind : 'value',
2956
+ source: transform.StringLiteral(node.source),
2957
+ specifiers
2958
+ }] : [];
2959
+ return [...out, ...unsupportedSpecifiers];
2960
+ },
2961
+
2962
+ InterfaceExtends(node) {
2963
+ return {
2964
+ type: 'TSInterfaceHeritage',
2965
+ loc: DUMMY_LOC,
2966
+ expression: node.id.type === 'QualifiedTypeIdentifier' ? transform.QualifiedTypeIdentifier(node.id) : transform.Identifier(node.id, false),
2967
+ typeParameters: transform.TypeParameterInstantiation(node.typeParameters)
2968
+ };
2969
+ },
2970
+
2971
+ InterfaceTypeAnnotation(node) {
2972
+ if (node.extends) {
2973
+ // type T = interface extends U, V { ... }
2974
+ // to
2975
+ // type T = U & V & { ... }
2976
+ return {
2977
+ type: 'TSIntersectionType',
2978
+ loc: DUMMY_LOC,
2979
+ types: [...node.extends.map(ex => ({
2980
+ type: 'TSTypeReference',
2981
+ loc: DUMMY_LOC,
2982
+ // Bug: ex.id can be qualified
2983
+ typeName: transform.Identifier(ex.id, false),
2984
+ typeParameters: transform.TypeParameterInstantiation(ex.typeParameters)
2985
+ })), transform.ObjectTypeAnnotation(node.body)]
2986
+ };
2987
+ }
2988
+
2989
+ return transform.ObjectTypeAnnotation(node.body);
2990
+ },
2991
+
2992
+ IntersectionTypeAnnotation(node) {
2993
+ return {
2994
+ type: 'TSIntersectionType',
2995
+ loc: DUMMY_LOC,
2996
+ types: node.types.map(transformTypeAnnotationType)
2997
+ };
2998
+ },
2999
+
3000
+ Literal(node) {
3001
+ switch (node.literalType) {
3002
+ case 'bigint':
3003
+ return transform.BigIntLiteral(node);
3004
+
3005
+ case 'boolean':
3006
+ return transform.BooleanLiteral(node);
3007
+
3008
+ case 'null':
3009
+ return transform.NullLiteral(node);
3010
+
3011
+ case 'numeric':
3012
+ return transform.NumericLiteral(node);
3013
+
3014
+ case 'regexp':
3015
+ return transform.RegExpLiteral(node);
3016
+
3017
+ case 'string':
3018
+ return transform.StringLiteral(node);
3019
+ }
3020
+ },
3021
+
3022
+ MixedTypeAnnotation(_node) {
3023
+ return {
3024
+ type: 'TSUnknownKeyword',
3025
+ loc: DUMMY_LOC
3026
+ };
3027
+ },
3028
+
3029
+ NullLiteral(_node) {
3030
+ return {
3031
+ type: 'Literal',
3032
+ loc: DUMMY_LOC,
3033
+ raw: 'null',
3034
+ value: null
3035
+ };
3036
+ },
3037
+
3038
+ NullLiteralTypeAnnotation(_node) {
3039
+ return {
3040
+ type: 'TSNullKeyword',
3041
+ loc: DUMMY_LOC
3042
+ };
3043
+ },
3044
+
3045
+ NullableTypeAnnotation(node) {
3046
+ // TS doesn't support the maybe type, so have to explicitly union in `null | undefined`
3047
+ // `?T` becomes `null | undefined | T`
3048
+ return {
3049
+ type: 'TSUnionType',
3050
+ loc: DUMMY_LOC,
3051
+ types: [{
3052
+ type: 'TSNullKeyword',
3053
+ loc: DUMMY_LOC
3054
+ }, {
3055
+ type: 'TSUndefinedKeyword',
3056
+ loc: DUMMY_LOC
3057
+ }, transformTypeAnnotationType(node.typeAnnotation)]
3058
+ };
3059
+ },
3060
+
3061
+ NumberLiteralTypeAnnotation(node) {
3062
+ return {
3063
+ type: 'TSLiteralType',
3064
+ loc: DUMMY_LOC,
3065
+ literal: {
3066
+ type: 'Literal',
3067
+ loc: DUMMY_LOC,
3068
+ value: node.value,
3069
+ raw: node.raw
3070
+ }
3071
+ };
3072
+ },
3073
+
3074
+ NumberTypeAnnotation(_node) {
3075
+ return {
3076
+ type: 'TSNumberKeyword',
3077
+ loc: DUMMY_LOC
3078
+ };
3079
+ },
3080
+
3081
+ NumericLiteral(node) {
3082
+ return {
3083
+ type: 'Literal',
3084
+ loc: DUMMY_LOC,
3085
+ raw: node.raw,
3086
+ value: node.value
3087
+ };
3088
+ },
3089
+
3090
+ ObjectTypeAnnotation(node) {
3091
+ if (node.properties.length === 1 && node.properties[0].type === 'ObjectTypeMappedTypeProperty') {
3092
+ var _prop$variance;
3093
+
3094
+ // Mapped Object Object types must not have other object properties.
3095
+ const prop = node.properties[0];
3096
+ const tsProp = {
3097
+ type: 'TSMappedType',
3098
+ loc: DUMMY_LOC,
3099
+ typeParameter: {
3100
+ type: 'TSTypeParameter',
3101
+ loc: DUMMY_LOC,
3102
+ name: {
3103
+ type: 'Identifier',
3104
+ loc: DUMMY_LOC,
3105
+ name: prop.keyTparam.name
3106
+ },
3107
+ constraint: transformTypeAnnotationType(prop.sourceType),
3108
+ in: false,
3109
+ out: false
3110
+ },
3111
+ readonly: ((_prop$variance = prop.variance) == null ? void 0 : _prop$variance.kind) === 'plus',
3112
+ optional: prop.optional === 'Optional',
3113
+ typeAnnotation: transformTypeAnnotationType(prop.propType),
3114
+ nameType: null
3115
+ };
3116
+ return tsProp;
3117
+ } // we want to preserve the source order of the members
3118
+ // unfortunately flow has unordered properties storing things
3119
+ // so store all elements with their start index and sort the
3120
+ // list afterward
3121
+
3122
+
3123
+ const members = [];
3124
+
3125
+ for (const callProp of node.callProperties) {
3126
+ members.push({
3127
+ start: callProp.range[0],
3128
+ node: transform.ObjectTypeCallProperty(callProp)
3129
+ });
3130
+ }
3131
+
3132
+ for (const indexer of node.indexers) {
3133
+ members.push({
3134
+ start: indexer.range[0],
3135
+ node: transform.ObjectTypeIndexer(indexer)
3136
+ });
3137
+ }
3138
+ /*
3139
+ TODO - internalSlots
3140
+ I don't think there's anything analogous in TS.
3141
+ They're really rarely used (if ever) - so let's just ignore them for now
3142
+ */
3143
+
3144
+
3145
+ if (node.internalSlots.length > 0) {
3146
+ return unsupportedAnnotation(node.internalSlots[0], 'internal slots');
3147
+ }
3148
+
3149
+ if (!node.properties.find(FlowESTree.isObjectTypeSpreadProperty)) {
3150
+ for (const property of node.properties) {
3151
+ if (property.type === 'ObjectTypeSpreadProperty') {
3152
+ // this is imposible due to the above find condition
3153
+ // this check is purely to satisfy flow
3154
+ throw unexpectedTranslationError(property, 'Impossible state');
3155
+ }
3156
+
3157
+ if (property.type === 'ObjectTypeMappedTypeProperty') {
3158
+ return unsupportedAnnotation(property, 'object type with mapped type property along with other properties');
3159
+ }
3160
+
3161
+ members.push({
3162
+ start: property.range[0],
3163
+ node: transform.ObjectTypeProperty(property)
3164
+ });
3165
+ }
3166
+
3167
+ const tsBody = members.sort((a, b) => a.start - b.start).map(({
3168
+ node
3169
+ }) => node);
3170
+ return {
3171
+ type: 'TSTypeLiteral',
3172
+ loc: DUMMY_LOC,
3173
+ members: tsBody
3174
+ };
3175
+ } else {
3176
+ /*
3177
+ spreads are a complicate thing for us to handle, sadly.
3178
+ in flow type spreads are modelled after object spreads; meaning that for
3179
+ { ...A, ...B } - all properties in B will explicitly replace any properties
3180
+ in A of the same name.
3181
+ ```
3182
+ type T1 = { a: string };
3183
+ type T2 = { a: number };
3184
+ type T3 = { ...T1, ...T2 };
3185
+ type A = T3['a'] // === number
3186
+ ```
3187
+ however in TS there are no object type spreads - you can only merge
3188
+ objects either via the intersection operator or via interface extends.
3189
+ For an interface extends - `interface B extends A { ... }` - TS enforces
3190
+ that the properties of B are all covariantly related to the same named
3191
+ properties in A.
3192
+ So we can't use an interface extends.
3193
+ For a type intersection - `type T = A & B;` - TS will (essentially) merge
3194
+ the types by intersecting each same named property in each type to calculate
3195
+ the resulting type. This has the effect of enforcing the same constraint
3196
+ as the interface case, however instead of an error it causes properties to
3197
+ become `never`:
3198
+ ```
3199
+ type T1 = { a: string };
3200
+ type T2 = { a: number };
3201
+ type T3 = T1 & T2;
3202
+ type A = T3['a'] // === string & number === never
3203
+ ```
3204
+ So in order for us to model flow's spreads we have to explicitly omit the keys
3205
+ from the proceeding type that might clash. We can do this pretty easily using
3206
+ TS's utility types:
3207
+ ```
3208
+ type T1 = { a: string };
3209
+ type T2 = { a: number };
3210
+ type T3 = Omit<T1, keyof T2> & T2;
3211
+ type A = T3['a'] // === number
3212
+ ```
3213
+ Unfortunately because we need to solve for the general case object type usage,
3214
+ it's going to be a bit ugly and complicated, sadly.
3215
+ If we had access to type information we would be able to skip some ugliness by
3216
+ checking to see if there is any overlap and skipping the omit step if there isn't.
3217
+ But alas - we're working purely syntactically.
3218
+ ```
3219
+ type T = { ...T1, b: string };
3220
+ // becomes
3221
+ type T = Omit<T1, keyof { b: string }> & { b: string };
3222
+ ```
3223
+ ```
3224
+ type T = { ...T1, ...T2, ...T3, b: string };
3225
+ // becomes
3226
+ type T =
3227
+ & Omit<T1, keyof T2 | keyof T3 | keyof { b: string }>
3228
+ & Omit<T2, keyof T3 | keyof { b: string }>
3229
+ & Omit<T3, keyof { b: string }>
3230
+ & { b: string };
3231
+ ```
3232
+ Note that because it's going to be super ugly and complicated - for now we're going to disallow:
3233
+ - spreads in the middle
3234
+ - spreads at the end
3235
+ - spreads of things that aren't "Identifiers"
3236
+ */
3237
+ if (members.length > 0) {
3238
+ return unsupportedAnnotation(node, 'object types with spreads, indexers and/or call properties at the same time');
3239
+ }
3240
+
3241
+ const typesToIntersect = [];
3242
+
3243
+ for (const property of node.properties) {
3244
+ if (property.type === 'ObjectTypeSpreadProperty') {
3245
+ if (members.length > 0) {
3246
+ return unsupportedAnnotation(property, 'object types with spreads in the middle or at the end');
3247
+ }
3248
+
3249
+ const spreadType = transformTypeAnnotationType(property.argument);
3250
+
3251
+ if (spreadType.type !== 'TSTypeReference' && spreadType.type !== 'TSTypeQuery') {
3252
+ return unsupportedAnnotation(property, 'object types with complex spreads');
3253
+ }
3254
+
3255
+ typesToIntersect.push(spreadType);
3256
+ } else if (property.type === 'ObjectTypeMappedTypeProperty') {
3257
+ // TODO - Support mapped types
3258
+ return unsupportedAnnotation(property, 'object type with mapped type property');
3259
+ } else {
3260
+ members.push({
3261
+ start: property.range[0],
3262
+ node: transform.ObjectTypeProperty(property)
3263
+ });
3264
+ }
3265
+ }
3266
+
3267
+ const tsBody = members.sort((a, b) => a.start - b.start).map(({
3268
+ node
3269
+ }) => node);
3270
+ const objectType = {
3271
+ type: 'TSTypeLiteral',
3272
+ loc: DUMMY_LOC,
3273
+ members: tsBody
3274
+ };
3275
+ const intersectionMembers = [];
3276
+
3277
+ for (let i = 0; i < typesToIntersect.length; i += 1) {
3278
+ const currentType = typesToIntersect[i];
3279
+ const remainingTypes = typesToIntersect.slice(i + 1);
3280
+ intersectionMembers.push({
3281
+ type: 'TSTypeReference',
3282
+ loc: DUMMY_LOC,
3283
+ typeName: {
3284
+ type: 'Identifier',
3285
+ loc: DUMMY_LOC,
3286
+ name: 'Omit'
3287
+ },
3288
+ typeParameters: {
3289
+ type: 'TSTypeParameterInstantiation',
3290
+ loc: DUMMY_LOC,
3291
+ params: [currentType, {
3292
+ type: 'TSUnionType',
3293
+ loc: DUMMY_LOC,
3294
+ types: [...remainingTypes.map(t => ({
3295
+ type: 'TSTypeOperator',
3296
+ loc: DUMMY_LOC,
3297
+ operator: 'keyof',
3298
+ typeAnnotation: t
3299
+ })), {
3300
+ type: 'TSTypeOperator',
3301
+ loc: DUMMY_LOC,
3302
+ operator: 'keyof',
3303
+ typeAnnotation: objectType
3304
+ }]
3305
+ }]
3306
+ }
3307
+ });
3308
+ }
3309
+
3310
+ intersectionMembers.push(objectType);
3311
+ return {
3312
+ type: 'TSIntersectionType',
3313
+ loc: DUMMY_LOC,
3314
+ types: intersectionMembers
3315
+ };
3316
+ }
3317
+ },
3318
+
3319
+ ObjectTypeCallProperty(node) {
3320
+ // the info is stored on the "value"
3321
+ const func = transform.FunctionTypeAnnotation(node.value);
3322
+ return {
3323
+ type: 'TSCallSignatureDeclaration',
3324
+ loc: DUMMY_LOC,
3325
+ params: func.params,
3326
+ returnType: func.returnType,
3327
+ typeParameters: func.typeParameters
3328
+ };
3329
+ },
3330
+
3331
+ ObjectTypeIndexer(node) {
3332
+ var _node$variance2;
3333
+
3334
+ if (node.key.type === 'GenericTypeAnnotation') {
3335
+ var _node$variance;
3336
+
3337
+ const ident = node.key.id.type === 'Identifier' ? node.key.id : node.key.id.id;
3338
+ return {
3339
+ type: 'TSPropertySignature',
3340
+ computed: true,
3341
+ loc: DUMMY_LOC,
3342
+ key: {
3343
+ type: 'BinaryExpression',
3344
+ operator: 'in',
3345
+ loc: DUMMY_LOC,
3346
+ left: {
3347
+ type: 'Identifier',
3348
+ name: node.id == null ? '$$Key$$' : node.id.name,
3349
+ loc: DUMMY_LOC
3350
+ },
3351
+ right: {
3352
+ type: 'Identifier',
3353
+ name: ident.name,
3354
+ loc: DUMMY_LOC
3355
+ }
3356
+ },
3357
+ readonly: ((_node$variance = node.variance) == null ? void 0 : _node$variance.kind) === 'plus',
3358
+ static: node.static,
3359
+ typeAnnotation: {
3360
+ type: 'TSTypeAnnotation',
3361
+ loc: DUMMY_LOC,
3362
+ typeAnnotation: transformTypeAnnotationType(node.value)
3363
+ }
3364
+ };
3365
+ }
3366
+
3367
+ return {
3368
+ type: 'TSIndexSignature',
3369
+ loc: DUMMY_LOC,
3370
+ parameters: [{
3371
+ type: 'Identifier',
3372
+ loc: DUMMY_LOC,
3373
+ name: node.id == null ? '$$Key$$' : node.id.name,
3374
+ typeAnnotation: {
3375
+ type: 'TSTypeAnnotation',
3376
+ loc: DUMMY_LOC,
3377
+ typeAnnotation: transformTypeAnnotationType(node.key)
3378
+ }
3379
+ }],
3380
+ readonly: ((_node$variance2 = node.variance) == null ? void 0 : _node$variance2.kind) === 'plus',
3381
+ static: node.static,
3382
+ typeAnnotation: {
3383
+ type: 'TSTypeAnnotation',
3384
+ loc: DUMMY_LOC,
3385
+ typeAnnotation: transformTypeAnnotationType(node.value)
3386
+ }
3387
+ };
3388
+ },
3389
+
3390
+ ObjectTypeProperty(node) {
3391
+ var _node$variance3;
3392
+
3393
+ const key = node.key.type === 'Identifier' ? transform.Identifier(node.key) : transform.StringLiteral(node.key);
3394
+
3395
+ if (node.method === true) {
3396
+ // flow has just one node for all object properties and relies upon the method flag
3397
+ // TS has separate nodes for methods and properties
3398
+ const func = transform.FunctionTypeAnnotation(node.value);
3399
+ return {
3400
+ type: 'TSMethodSignature',
3401
+ loc: DUMMY_LOC,
3402
+ computed: false,
3403
+ key,
3404
+ kind: node.kind === 'init' ? 'method' : node.kind,
3405
+ optional: node.optional,
3406
+ params: func.params,
3407
+ returnType: func.returnType,
3408
+ static: node.static,
3409
+ typeParameters: func.typeParameters
3410
+ };
3411
+ }
3412
+
3413
+ if (node.kind === 'get' || node.kind === 'set') {
3414
+ // flow treats getters/setters as true property signatures (method === false)
3415
+ // TS treats them as method signatures
3416
+ const func = transform.FunctionTypeAnnotation(node.value);
3417
+ return {
3418
+ type: 'TSMethodSignature',
3419
+ loc: DUMMY_LOC,
3420
+ computed: false,
3421
+ key,
3422
+ kind: node.kind,
3423
+ optional: false,
3424
+ params: func.params,
3425
+ // TS setters must not have a return type
3426
+ returnType: node.kind === 'set' ? undefined : func.returnType,
3427
+ static: node.static,
3428
+ // TS accessors cannot have type parameters
3429
+ typeParameters: undefined
3430
+ };
3431
+ }
3432
+
3433
+ return {
3434
+ type: 'TSPropertySignature',
3435
+ loc: DUMMY_LOC,
3436
+ computed: false,
3437
+ key,
3438
+ optional: node.optional,
3439
+ readonly: ((_node$variance3 = node.variance) == null ? void 0 : _node$variance3.kind) === 'plus',
3440
+ static: node.static,
3441
+ typeAnnotation: {
3442
+ type: 'TSTypeAnnotation',
3443
+ loc: DUMMY_LOC,
3444
+ typeAnnotation: transformTypeAnnotationType(node.value)
3445
+ }
3446
+ };
3447
+ },
3448
+
3449
+ OpaqueType(node) {
3450
+ return transform.DeclareOpaqueType(node);
3451
+ },
3452
+
3453
+ OptionalIndexedAccessType(node) {
3454
+ // Foo?.[A][B]
3455
+ // ^^^^^^^^ optional = true
3456
+ // ^^^^^^^^^^^ optional = false
3457
+ if (node.optional === false) {
3458
+ return transform.IndexedAccessType(node);
3459
+ } // TS doesn't support optional index access so we have to wrap the object:
3460
+ // `T?.[K]` becomes `NonNullable<T>[K]`
3461
+
3462
+
3463
+ return {
3464
+ type: 'TSIndexedAccessType',
3465
+ loc: DUMMY_LOC,
3466
+ objectType: {
3467
+ type: 'TSTypeReference',
3468
+ loc: DUMMY_LOC,
3469
+ typeName: {
3470
+ type: 'Identifier',
3471
+ loc: DUMMY_LOC,
3472
+ name: 'NonNullable'
3473
+ },
3474
+ typeParameters: {
3475
+ type: 'TSTypeParameterInstantiation',
3476
+ loc: DUMMY_LOC,
3477
+ params: [transformTypeAnnotationType(node.objectType)]
3478
+ }
3479
+ },
3480
+ indexType: transformTypeAnnotationType(node.indexType)
3481
+ };
3482
+ },
3483
+
3484
+ QualifiedTypeIdentifier(node) {
3485
+ const qual = node.qualification;
3486
+ return {
3487
+ type: 'TSQualifiedName',
3488
+ loc: DUMMY_LOC,
3489
+ left: qual.type === 'Identifier' ? transform.Identifier(qual, false) : transform.QualifiedTypeIdentifier(qual),
3490
+ right: transform.Identifier(node.id, false)
3491
+ };
3492
+ },
3493
+
3494
+ QualifiedTypeofIdentifier(node) {
3495
+ const qual = node.qualification;
3496
+ return {
3497
+ type: 'TSQualifiedName',
3498
+ loc: DUMMY_LOC,
3499
+ left: qual.type === 'Identifier' ? transform.Identifier(qual, false) : transform.QualifiedTypeofIdentifier(qual),
3500
+ right: transform.Identifier(node.id, false)
3501
+ };
3502
+ },
3503
+
3504
+ RegExpLiteral(node) {
3505
+ return {
3506
+ type: 'Literal',
3507
+ loc: DUMMY_LOC,
3508
+ raw: node.raw,
3509
+ regex: {
3510
+ pattern: node.regex.pattern,
3511
+ flags: node.regex.pattern
3512
+ },
3513
+ value: node.value
3514
+ };
3515
+ },
3516
+
3517
+ StringLiteral(node) {
3518
+ return {
3519
+ type: 'Literal',
3520
+ loc: DUMMY_LOC,
3521
+ raw: node.raw,
3522
+ value: node.value
3523
+ };
3524
+ },
3525
+
3526
+ StringLiteralTypeAnnotation(node) {
3527
+ return {
3528
+ type: 'TSLiteralType',
3529
+ loc: DUMMY_LOC,
3530
+ literal: {
3531
+ type: 'Literal',
3532
+ loc: DUMMY_LOC,
3533
+ value: node.value,
3534
+ raw: node.raw
3535
+ }
3536
+ };
3537
+ },
3538
+
3539
+ StringTypeAnnotation(_node) {
3540
+ return {
3541
+ type: 'TSStringKeyword',
3542
+ loc: DUMMY_LOC
3543
+ };
3544
+ },
3545
+
3546
+ SymbolTypeAnnotation(_node) {
3547
+ return {
3548
+ type: 'TSSymbolKeyword',
3549
+ loc: DUMMY_LOC
3550
+ };
3551
+ },
3552
+
3553
+ ThisTypeAnnotation(_node) {
3554
+ return {
3555
+ type: 'TSThisType',
3556
+ loc: DUMMY_LOC
3557
+ };
3558
+ },
3559
+
3560
+ TupleTypeAnnotation(node) {
3561
+ const allReadOnly = node.types.length > 0 && node.types.every(element => element.type === 'TupleTypeLabeledElement' && element.variance != null && element.variance.kind === 'plus');
3562
+ const elems = node.types.map(element => {
3563
+ switch (element.type) {
3564
+ case 'TupleTypeLabeledElement':
3565
+ if (!allReadOnly && element.variance != null) {
3566
+ return unsupportedAnnotation(element, 'tuple type element variance annotations');
3567
+ }
3568
+
3569
+ return {
3570
+ type: 'TSNamedTupleMember',
3571
+ loc: DUMMY_LOC,
3572
+ label: transform.Identifier(element.label),
3573
+ optional: element.optional,
3574
+ elementType: transformTypeAnnotationType(element.elementType)
3575
+ };
3576
+
3577
+ case 'TupleTypeSpreadElement':
3578
+ {
3579
+ const annot = transformTypeAnnotationType(element.typeAnnotation);
3580
+ return {
3581
+ type: 'TSRestType',
3582
+ loc: DUMMY_LOC,
3583
+ typeAnnotation: element.label != null ? {
3584
+ type: 'TSNamedTupleMember',
3585
+ loc: DUMMY_LOC,
3586
+ label: transform.Identifier(element.label),
3587
+ optional: false,
3588
+ elementType: annot
3589
+ } : annot
3590
+ };
3591
+ }
3592
+
3593
+ default:
3594
+ return transformTypeAnnotationType(element);
3595
+ }
3596
+ });
3597
+ const elementTypes = node.inexact ? [...elems, {
3598
+ type: 'TSRestType',
3599
+ loc: DUMMY_LOC,
3600
+ typeAnnotation: {
3601
+ type: 'TSArrayType',
3602
+ loc: DUMMY_LOC,
3603
+ elementType: {
3604
+ type: 'TSUnknownKeyword',
3605
+ loc: DUMMY_LOC
3606
+ }
3607
+ }
3608
+ }] : elems;
3609
+ const tupleAnnot = {
3610
+ type: 'TSTupleType',
3611
+ loc: DUMMY_LOC,
3612
+ elementTypes
3613
+ };
3614
+ return allReadOnly ? {
3615
+ type: 'TSTypeOperator',
3616
+ loc: DUMMY_LOC,
3617
+ operator: 'readonly',
3618
+ typeAnnotation: tupleAnnot
3619
+ } : tupleAnnot;
3620
+ },
3621
+
3622
+ TypeAlias(node) {
3623
+ return transform.DeclareTypeAlias(node);
3624
+ },
3625
+
3626
+ TypeAnnotation(node) {
3627
+ return {
3628
+ type: 'TSTypeAnnotation',
3629
+ loc: DUMMY_LOC,
3630
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation)
3631
+ };
3632
+ },
3633
+
3634
+ TypeofTypeAnnotation(node) {
3635
+ switch (node.argument.type) {
3636
+ case 'Identifier':
3637
+ return {
3638
+ type: 'TSTypeQuery',
3639
+ loc: DUMMY_LOC,
3640
+ exprName: transform.Identifier(node.argument),
3641
+ typeParameters: undefined
3642
+ };
3643
+
3644
+ case 'QualifiedTypeofIdentifier':
3645
+ return {
3646
+ type: 'TSTypeQuery',
3647
+ loc: DUMMY_LOC,
3648
+ exprName: transform.QualifiedTypeofIdentifier(node.argument),
3649
+ typeParameters: undefined
3650
+ };
3651
+ }
3652
+ },
3653
+
3654
+ TypeParameter(node) {
3655
+ /*
3656
+ TODO - flow models variance as explicit syntax, but but TS resolves it automatically
3657
+ TS does have syntax for explicit variance, but you can introduce a TS error if the
3658
+ marked parameter isn't used in the location that TS expects them to be in.
3659
+ To make it easier for now let's just rely on TS's auto-resolution.
3660
+ ```
3661
+ const variance =
3662
+ new Set(
3663
+ node.variance == null
3664
+ ? // by default flow generics act invariantly
3665
+ ['in', 'out']
3666
+ : node.variance.kind === 'plus'
3667
+ ? // covariant
3668
+ ['out']
3669
+ : // contravariant
3670
+ ['in'],
3671
+ );
3672
+ ```
3673
+ */
3674
+ return {
3675
+ type: 'TSTypeParameter',
3676
+ loc: DUMMY_LOC,
3677
+ name: {
3678
+ type: 'Identifier',
3679
+ loc: DUMMY_LOC,
3680
+ name: node.name
3681
+ },
3682
+ constraint: node.bound == null ? undefined : transformTypeAnnotationType(node.bound.typeAnnotation),
3683
+ default: node.default == null ? undefined : transformTypeAnnotationType(node.default),
3684
+ in: false,
3685
+ out: false // in: variance.has('in'),
3686
+ // out: variance.has('out'),
3687
+
3688
+ };
3689
+ },
3690
+
3691
+ TypeParameterDeclaration(node) {
3692
+ return {
3693
+ type: 'TSTypeParameterDeclaration',
3694
+ loc: DUMMY_LOC,
3695
+ params: node.params.map(transform.TypeParameter)
3696
+ };
3697
+ },
3698
+
3699
+ TypeParameterInstantiation(node) {
3700
+ // Empty parameters in Flow are valid, but TS requires at least one parameter.
3701
+ // This ensures empty type parameters are not created in TS
3702
+ if (node == null || node.params.length === 0) {
3703
+ return undefined;
3704
+ }
3705
+
3706
+ return {
3707
+ type: 'TSTypeParameterInstantiation',
3708
+ loc: DUMMY_LOC,
3709
+ params: node.params.map(transformTypeAnnotationType)
3710
+ };
3711
+ },
3712
+
3713
+ UnionTypeAnnotation(node) {
3714
+ return {
3715
+ type: 'TSUnionType',
3716
+ loc: DUMMY_LOC,
3717
+ types: node.types.map(transformTypeAnnotationType)
3718
+ };
3719
+ },
3720
+
3721
+ VoidTypeAnnotation(_node) {
3722
+ return {
3723
+ type: 'TSVoidKeyword',
3724
+ loc: DUMMY_LOC
3725
+ };
3726
+ },
3727
+
3728
+ ConditionalTypeAnnotation(node) {
3729
+ return {
3730
+ type: 'TSConditionalType',
3731
+ loc: DUMMY_LOC,
3732
+ checkType: transformTypeAnnotationType(node.checkType),
3733
+ extendsType: transformTypeAnnotationType(node.extendsType),
3734
+ trueType: transformTypeAnnotationType(node.trueType),
3735
+ falseType: transformTypeAnnotationType(node.falseType)
3736
+ };
3737
+ },
3738
+
3739
+ TypePredicateAnnotation(node) {
3740
+ return {
3741
+ type: 'TSTypePredicate',
3742
+ loc: DUMMY_LOC,
3743
+ asserts: node.kind != null && node.kind === 'asserts',
3744
+ parameterName: transform.Identifier(node.parameterName, false),
3745
+ typeAnnotation: node.typeAnnotation && {
3746
+ type: 'TSTypeAnnotation',
3747
+ loc: DUMMY_LOC,
3748
+ typeAnnotation: transformTypeAnnotationType(node.typeAnnotation)
3749
+ }
3750
+ };
3751
+ },
3752
+
3753
+ InferTypeAnnotation(node) {
3754
+ return {
3755
+ type: 'TSInferType',
3756
+ loc: DUMMY_LOC,
3757
+ typeParameter: transform.TypeParameter(node.typeParameter)
3758
+ };
3759
+ },
3760
+
3761
+ KeyofTypeAnnotation(node) {
3762
+ return {
3763
+ type: 'TSTypeOperator',
3764
+ loc: DUMMY_LOC,
3765
+ operator: 'keyof',
3766
+ typeAnnotation: transformTypeAnnotationType(node.argument)
3767
+ };
3768
+ },
3769
+
3770
+ TypeOperator(node) {
3771
+ switch (node.operator) {
3772
+ case 'renders':
3773
+ case 'renders?':
3774
+ case 'renders*':
3775
+ {
3776
+ const hasReactImport = isReactImport(node, 'React');
3777
+ return {
3778
+ type: 'TSTypeReference',
3779
+ loc: DUMMY_LOC,
3780
+ typeName: {
3781
+ type: 'TSQualifiedName',
3782
+ loc: DUMMY_LOC,
3783
+ left: getReactIdentifier(hasReactImport),
3784
+ right: {
3785
+ type: 'Identifier',
3786
+ loc: DUMMY_LOC,
3787
+ name: `ReactNode`
3788
+ }
3789
+ },
3790
+ typeParameters: undefined
3791
+ };
3792
+ }
3793
+ }
3794
+ },
3795
+
3796
+ ComponentTypeAnnotation(node) {
3797
+ const typeParameters = node.typeParameters == null ? undefined : transform.TypeParameterDeclaration(node.typeParameters);
3798
+ const params = transform.ComponentTypeParameters(node.params, node.rest); // TS cannot support `renderType` so we always use ReactNode as the return type.
3799
+
3800
+ const hasReactImport = isReactImport(node, 'React');
3801
+ const returnType = {
3802
+ type: 'TSTypeAnnotation',
3803
+ loc: DUMMY_LOC,
3804
+ // If no rendersType we assume its ReactNode type.
3805
+ typeAnnotation: {
3806
+ type: 'TSTypeReference',
3807
+ loc: DUMMY_LOC,
3808
+ typeName: {
3809
+ type: 'TSQualifiedName',
3810
+ loc: DUMMY_LOC,
3811
+ left: getReactIdentifier(hasReactImport),
3812
+ right: {
3813
+ type: 'Identifier',
3814
+ loc: DUMMY_LOC,
3815
+ name: `ReactNode`
3816
+ }
3817
+ },
3818
+ typeParameters: undefined
3819
+ }
3820
+ };
3821
+ return {
3822
+ type: 'TSFunctionType',
3823
+ loc: DUMMY_LOC,
3824
+ typeParameters,
3825
+ params,
3826
+ returnType
3827
+ };
3828
+ }
3829
+
3830
+ }; // wrap each transform so that we automatically preserve jsdoc comments
3831
+ // this just saves us manually wiring up every single case
3832
+
3833
+ for (const key of Object.keys(transform)) {
3834
+ const originalFn = transform[key]; // $FlowExpectedError[cannot-write]
3835
+ // $FlowExpectedError[missing-local-annot]
3836
+
3837
+ transform[key] = (node, ...args) => {
3838
+ const result = originalFn(node, ...args);
3839
+
3840
+ if (node != null && result != null) {
3841
+ if (Array.isArray(result)) {
3842
+ cloneJSDocCommentsToNewNode(node, result[0]);
3843
+ } else {
3844
+ cloneJSDocCommentsToNewNode(node, result);
3845
+ }
3846
+ }
3847
+
3848
+ return result;
3849
+ };
3850
+ }
3851
+
3852
+ return [transform, code];
3853
+ };