flow-api-translator 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flowDefToTSDef.js +1052 -617
- package/dist/flowDefToTSDef.js.flow +1128 -612
- package/dist/flowToFlowDef.js +2 -1
- package/dist/flowToFlowDef.js.flow +1 -0
- package/dist/index.js +4 -2
- package/dist/index.js.flow +4 -2
- package/dist/utils/ErrorUtils.js +3 -2
- package/dist/utils/ErrorUtils.js.flow +5 -4
- package/dist/utils/ts-estree-ast-types.js.flow +1 -1
- package/package.json +5 -4
|
@@ -13,25 +13,44 @@
|
|
|
13
13
|
import type {ObjectWithLoc} from 'hermes-estree';
|
|
14
14
|
import * as FlowESTree from 'hermes-estree';
|
|
15
15
|
import type {ScopeManager} from 'hermes-eslint';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
cloneJSDocCommentsToNewNode as cloneJSDocCommentsToNewNodeOriginal,
|
|
18
|
+
makeCommentOwnLine as makeCommentOwnLineOriginal,
|
|
19
|
+
} from 'hermes-transform';
|
|
17
20
|
import * as TSESTree from './utils/ts-estree-ast-types';
|
|
18
21
|
import {
|
|
22
|
+
buildCodeFrame,
|
|
19
23
|
translationError as translationErrorBase,
|
|
20
24
|
unexpectedTranslationError as unexpectedTranslationErrorBase,
|
|
21
25
|
} from './utils/ErrorUtils';
|
|
22
26
|
import {removeAtFlowFromDocblock} from './utils/DocblockUtils';
|
|
27
|
+
import type {TranslationOptions} from './utils/TranslationUtils';
|
|
28
|
+
import {EOL} from 'os';
|
|
29
|
+
|
|
30
|
+
type DeclarationOrUnsupported<T> = T | TSESTree.TSTypeAliasDeclaration;
|
|
23
31
|
|
|
24
32
|
const cloneJSDocCommentsToNewNode =
|
|
25
33
|
// $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
|
|
26
34
|
(cloneJSDocCommentsToNewNodeOriginal: (mixed, mixed) => void);
|
|
27
35
|
|
|
36
|
+
const makeCommentOwnLine =
|
|
37
|
+
// $FlowExpectedError[incompatible-cast] - trust me this re-type is 100% safe
|
|
38
|
+
(makeCommentOwnLineOriginal: (string, mixed) => string);
|
|
39
|
+
|
|
28
40
|
const VALID_REACT_IMPORTS = new Set<string>(['React', 'react']);
|
|
29
41
|
|
|
42
|
+
function isValidReactImportOrGlobal(id: FlowESTree.Identifier): boolean {
|
|
43
|
+
return VALID_REACT_IMPORTS.has(id.name) || id.name.startsWith('React$');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let shouldAddReactImport: boolean | null = null;
|
|
47
|
+
|
|
30
48
|
export function flowDefToTSDef(
|
|
31
49
|
originalCode: string,
|
|
32
50
|
ast: FlowESTree.Program,
|
|
33
51
|
scopeManager: ScopeManager,
|
|
34
|
-
|
|
52
|
+
opts: TranslationOptions,
|
|
53
|
+
): [TSESTree.Program, string] {
|
|
35
54
|
const tsBody: Array<TSESTree.ProgramStatement> = [];
|
|
36
55
|
const tsProgram: TSESTree.Program = {
|
|
37
56
|
type: 'Program',
|
|
@@ -41,7 +60,9 @@ export function flowDefToTSDef(
|
|
|
41
60
|
ast.docblock == null ? null : removeAtFlowFromDocblock(ast.docblock),
|
|
42
61
|
};
|
|
43
62
|
|
|
44
|
-
|
|
63
|
+
shouldAddReactImport = null;
|
|
64
|
+
|
|
65
|
+
const [transform, code] = getTransforms(originalCode, scopeManager, opts);
|
|
45
66
|
|
|
46
67
|
for (const node of ast.body) {
|
|
47
68
|
if (node.type in transform) {
|
|
@@ -58,26 +79,117 @@ export function flowDefToTSDef(
|
|
|
58
79
|
throw unexpectedTranslationErrorBase(
|
|
59
80
|
node,
|
|
60
81
|
`Unexpected node type ${node.type}`,
|
|
61
|
-
{code
|
|
82
|
+
{code},
|
|
62
83
|
);
|
|
63
84
|
}
|
|
64
85
|
}
|
|
65
86
|
|
|
66
|
-
|
|
87
|
+
if (shouldAddReactImport === true) {
|
|
88
|
+
tsBody.unshift({
|
|
89
|
+
type: 'ImportDeclaration',
|
|
90
|
+
assertions: [],
|
|
91
|
+
source: {
|
|
92
|
+
type: 'Literal',
|
|
93
|
+
value: 'react',
|
|
94
|
+
raw: "'react'",
|
|
95
|
+
},
|
|
96
|
+
specifiers: [
|
|
97
|
+
{
|
|
98
|
+
type: 'ImportNamespaceSpecifier',
|
|
99
|
+
local: {
|
|
100
|
+
type: 'Identifier',
|
|
101
|
+
name: 'React',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
importKind: 'value',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [tsProgram, code];
|
|
67
110
|
}
|
|
68
111
|
|
|
69
|
-
const getTransforms = (
|
|
112
|
+
const getTransforms = (
|
|
113
|
+
originalCode: string,
|
|
114
|
+
scopeManager: ScopeManager,
|
|
115
|
+
opts: TranslationOptions,
|
|
116
|
+
) => {
|
|
117
|
+
let code = originalCode;
|
|
70
118
|
function translationError(node: ObjectWithLoc, message: string) {
|
|
71
119
|
return translationErrorBase(node, message, {code});
|
|
72
120
|
}
|
|
73
121
|
function unexpectedTranslationError(node: ObjectWithLoc, message: string) {
|
|
74
122
|
return unexpectedTranslationErrorBase(node, message, {code});
|
|
75
123
|
}
|
|
76
|
-
function
|
|
77
|
-
return
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
);
|
|
124
|
+
function unsupportedFeatureMessage(thing: string) {
|
|
125
|
+
return `Unsupported feature: Translating "${thing}" is currently not supported.`;
|
|
126
|
+
}
|
|
127
|
+
function buildCodeFrameForComment(node: ObjectWithLoc, message: string) {
|
|
128
|
+
return buildCodeFrame(node, message, code, false);
|
|
129
|
+
}
|
|
130
|
+
function addErrorComment(node: TSESTree.Node, message: string): void {
|
|
131
|
+
const comment = {
|
|
132
|
+
type: 'Block',
|
|
133
|
+
value: `*${EOL} * ${message.replace(
|
|
134
|
+
new RegExp(EOL, 'g'),
|
|
135
|
+
`${EOL} * `,
|
|
136
|
+
)}${EOL}*`,
|
|
137
|
+
leading: true,
|
|
138
|
+
printed: false,
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
code = makeCommentOwnLine(code, comment);
|
|
142
|
+
|
|
143
|
+
// $FlowExpectedError[prop-missing]
|
|
144
|
+
// $FlowExpectedError[cannot-write]
|
|
145
|
+
node.comments ??= [];
|
|
146
|
+
// $FlowExpectedError[prop-missing]
|
|
147
|
+
// $FlowExpectedError[incompatible-cast]
|
|
148
|
+
(node.comments: Array<TSESTree.Comment>).push(comment);
|
|
149
|
+
}
|
|
150
|
+
function unsupportedAnnotation(
|
|
151
|
+
node: ObjectWithLoc,
|
|
152
|
+
thing: string,
|
|
153
|
+
): TSESTree.TSAnyKeyword {
|
|
154
|
+
const message = unsupportedFeatureMessage(thing);
|
|
155
|
+
if (opts.recoverFromErrors) {
|
|
156
|
+
const codeFrame = buildCodeFrameForComment(node, message);
|
|
157
|
+
const newNode = {
|
|
158
|
+
type: 'TSAnyKeyword',
|
|
159
|
+
};
|
|
160
|
+
addErrorComment(newNode, codeFrame);
|
|
161
|
+
return newNode;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
throw translationError(node, message);
|
|
165
|
+
}
|
|
166
|
+
function unsupportedDeclaration(
|
|
167
|
+
node: ObjectWithLoc,
|
|
168
|
+
thing: string,
|
|
169
|
+
id: FlowESTree.Identifier,
|
|
170
|
+
declare: boolean = false,
|
|
171
|
+
typeParameters: FlowESTree.TypeParameterDeclaration | null = null,
|
|
172
|
+
): TSESTree.TSTypeAliasDeclaration {
|
|
173
|
+
const message = unsupportedFeatureMessage(thing);
|
|
174
|
+
if (opts.recoverFromErrors) {
|
|
175
|
+
const codeFrame = buildCodeFrameForComment(node, message);
|
|
176
|
+
const newNode = {
|
|
177
|
+
type: 'TSTypeAliasDeclaration',
|
|
178
|
+
declare,
|
|
179
|
+
id: transform.Identifier(id, false),
|
|
180
|
+
typeAnnotation: {
|
|
181
|
+
type: 'TSAnyKeyword',
|
|
182
|
+
},
|
|
183
|
+
typeParameters:
|
|
184
|
+
typeParameters == null
|
|
185
|
+
? undefined
|
|
186
|
+
: transform.TypeParameterDeclaration(typeParameters),
|
|
187
|
+
};
|
|
188
|
+
addErrorComment(newNode, codeFrame);
|
|
189
|
+
return newNode;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
throw translationError(node, message);
|
|
81
193
|
}
|
|
82
194
|
|
|
83
195
|
const topScope = (() => {
|
|
@@ -92,7 +204,20 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
92
204
|
})();
|
|
93
205
|
|
|
94
206
|
function isReactImport(id: FlowESTree.Identifier): boolean {
|
|
95
|
-
let currentScope =
|
|
207
|
+
let currentScope = (() => {
|
|
208
|
+
let scope = null;
|
|
209
|
+
let node: FlowESTree.ESNode = id;
|
|
210
|
+
while (!scope && node) {
|
|
211
|
+
scope = scopeManager.acquire(node, true);
|
|
212
|
+
node = node.parent;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return scope;
|
|
216
|
+
})();
|
|
217
|
+
|
|
218
|
+
if (currentScope == null) {
|
|
219
|
+
throw new Error('unable to resolve scope');
|
|
220
|
+
}
|
|
96
221
|
|
|
97
222
|
const variableDef = (() => {
|
|
98
223
|
while (currentScope != null) {
|
|
@@ -105,9 +230,10 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
105
230
|
}
|
|
106
231
|
})();
|
|
107
232
|
|
|
108
|
-
// No variable found, it
|
|
233
|
+
// No variable found, it is not imported.
|
|
234
|
+
// It could be a global though if isValidReactImportOrGlobal returns true.
|
|
109
235
|
if (variableDef == null) {
|
|
110
|
-
return
|
|
236
|
+
return false;
|
|
111
237
|
}
|
|
112
238
|
|
|
113
239
|
const def = variableDef.defs[0];
|
|
@@ -138,6 +264,407 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
138
264
|
return false;
|
|
139
265
|
}
|
|
140
266
|
|
|
267
|
+
function EnumImpl(
|
|
268
|
+
node: FlowESTree.EnumDeclaration | FlowESTree.DeclareEnum,
|
|
269
|
+
): DeclarationOrUnsupported<
|
|
270
|
+
[TSESTree.TSEnumDeclaration, TSESTree.TSModuleDeclaration],
|
|
271
|
+
> {
|
|
272
|
+
const body = node.body;
|
|
273
|
+
if (body.type === 'EnumSymbolBody') {
|
|
274
|
+
/*
|
|
275
|
+
There's unfortunately no way for us to support this in a clean way.
|
|
276
|
+
We can get really close using this code:
|
|
277
|
+
```
|
|
278
|
+
declare namespace SymbolEnum {
|
|
279
|
+
export const member1: unique symbol;
|
|
280
|
+
export type member1 = typeof member1;
|
|
281
|
+
|
|
282
|
+
export const member2: unique symbol;
|
|
283
|
+
export type member2 = typeof member2;
|
|
284
|
+
}
|
|
285
|
+
type SymbolEnum = typeof SymbolEnum[keyof typeof SymbolEnum];
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
However as explained in https://github.com/microsoft/TypeScript/issues/43657:
|
|
289
|
+
"A unique symbol type is never transferred from one declaration to another through inference."
|
|
290
|
+
This intended behaviour in TS means that the usage of the fake-enum would look like this:
|
|
291
|
+
```
|
|
292
|
+
const value: SymbolEnum.member1 = SymbolEnum.member1;
|
|
293
|
+
// ^^^^^^^^^^^^^^^^^^ required to force TS to retain the information
|
|
294
|
+
```
|
|
295
|
+
Which is really clunky and shitty. It definitely works, but ofc it's not good.
|
|
296
|
+
We can go with this design if users are okay with it!
|
|
297
|
+
|
|
298
|
+
Considering how rarely used symbol enums are ATM, let's just put a pin in it for now.
|
|
299
|
+
*/
|
|
300
|
+
return unsupportedDeclaration(
|
|
301
|
+
node,
|
|
302
|
+
'symbol enums',
|
|
303
|
+
node.id,
|
|
304
|
+
FlowESTree.isDeclareEnum(node),
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
if (body.type === 'EnumBooleanBody') {
|
|
308
|
+
/*
|
|
309
|
+
TODO - TS enums only allow strings or numbers as their values - not booleans.
|
|
310
|
+
This means we need a non-ts-enum representation of the enum.
|
|
311
|
+
We can support boolean enums using a construct like this:
|
|
312
|
+
```ts
|
|
313
|
+
declare namespace BooleanEnum {
|
|
314
|
+
export const member1: true;
|
|
315
|
+
export type member1 = typeof member1;
|
|
316
|
+
|
|
317
|
+
export const member2: false;
|
|
318
|
+
export type member2 = typeof member1;
|
|
319
|
+
}
|
|
320
|
+
declare type BooleanEnum = boolean;
|
|
321
|
+
```
|
|
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(
|
|
327
|
+
node,
|
|
328
|
+
'boolean enums',
|
|
329
|
+
node.id,
|
|
330
|
+
FlowESTree.isDeclareEnum(node),
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const members: Array<TSESTree.TSEnumMemberNonComputedName> = [];
|
|
335
|
+
for (const member of body.members) {
|
|
336
|
+
switch (member.type) {
|
|
337
|
+
case 'EnumDefaultedMember': {
|
|
338
|
+
if (body.type === 'EnumNumberBody') {
|
|
339
|
+
// this should be impossible!
|
|
340
|
+
throw unexpectedTranslationError(
|
|
341
|
+
member,
|
|
342
|
+
'Unexpected defaulted number enum member',
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
members.push({
|
|
346
|
+
type: 'TSEnumMember',
|
|
347
|
+
computed: false,
|
|
348
|
+
id: transform.Identifier(member.id, false),
|
|
349
|
+
initializer: ({
|
|
350
|
+
type: 'Literal',
|
|
351
|
+
raw: `"${member.id.name}"`,
|
|
352
|
+
value: member.id.name,
|
|
353
|
+
}: TSESTree.StringLiteral),
|
|
354
|
+
});
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
case 'EnumNumberMember':
|
|
359
|
+
case 'EnumStringMember':
|
|
360
|
+
members.push({
|
|
361
|
+
type: 'TSEnumMember',
|
|
362
|
+
computed: false,
|
|
363
|
+
id: transform.Identifier(member.id, false),
|
|
364
|
+
initializer:
|
|
365
|
+
member.init.literalType === 'string'
|
|
366
|
+
? transform.StringLiteral(member.init)
|
|
367
|
+
: transform.NumericLiteral(member.init),
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const bodyRepresentationType =
|
|
373
|
+
body.type === 'EnumNumberBody'
|
|
374
|
+
? {type: 'TSNumberKeyword'}
|
|
375
|
+
: {type: 'TSStringKeyword'};
|
|
376
|
+
|
|
377
|
+
const enumName = transform.Identifier(node.id, false);
|
|
378
|
+
return [
|
|
379
|
+
{
|
|
380
|
+
type: 'TSEnumDeclaration',
|
|
381
|
+
const: false,
|
|
382
|
+
declare: true,
|
|
383
|
+
id: enumName,
|
|
384
|
+
members,
|
|
385
|
+
},
|
|
386
|
+
// flow also exports `.cast`, `.isValid`, `.members` and `.getName` for enums
|
|
387
|
+
// we can use declaration merging to declare these functions on the enum:
|
|
388
|
+
/*
|
|
389
|
+
declare enum Foo {
|
|
390
|
+
A = 1,
|
|
391
|
+
B = 2,
|
|
392
|
+
}
|
|
393
|
+
declare namespace Foo {
|
|
394
|
+
export function cast(value: number | null | undefined): Foo;
|
|
395
|
+
export function isValid(value: number | null | undefined): value is Foo;
|
|
396
|
+
export function members(): IterableIterator<Foo>;
|
|
397
|
+
export function getName(value: Foo): string;
|
|
398
|
+
}
|
|
399
|
+
*/
|
|
400
|
+
{
|
|
401
|
+
type: 'TSModuleDeclaration',
|
|
402
|
+
declare: true,
|
|
403
|
+
id: enumName,
|
|
404
|
+
body: {
|
|
405
|
+
type: 'TSModuleBlock',
|
|
406
|
+
body: [
|
|
407
|
+
// export function cast(value: number | null | undefined): Foo
|
|
408
|
+
{
|
|
409
|
+
type: 'ExportNamedDeclaration',
|
|
410
|
+
declaration: {
|
|
411
|
+
type: 'TSDeclareFunction',
|
|
412
|
+
id: {
|
|
413
|
+
type: 'Identifier',
|
|
414
|
+
name: 'cast',
|
|
415
|
+
},
|
|
416
|
+
generator: false,
|
|
417
|
+
expression: false,
|
|
418
|
+
async: false,
|
|
419
|
+
params: [
|
|
420
|
+
{
|
|
421
|
+
type: 'Identifier',
|
|
422
|
+
name: 'value',
|
|
423
|
+
typeAnnotation: {
|
|
424
|
+
type: 'TSTypeAnnotation',
|
|
425
|
+
typeAnnotation: {
|
|
426
|
+
type: 'TSUnionType',
|
|
427
|
+
types: [
|
|
428
|
+
bodyRepresentationType,
|
|
429
|
+
{
|
|
430
|
+
type: 'TSNullKeyword',
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
type: 'TSUndefinedKeyword',
|
|
434
|
+
},
|
|
435
|
+
],
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
returnType: {
|
|
441
|
+
type: 'TSTypeAnnotation',
|
|
442
|
+
typeAnnotation: {
|
|
443
|
+
type: 'TSTypeReference',
|
|
444
|
+
typeName: enumName,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
specifiers: [],
|
|
449
|
+
source: null,
|
|
450
|
+
exportKind: 'value',
|
|
451
|
+
assertions: [],
|
|
452
|
+
},
|
|
453
|
+
// export function isValid(value: number | null | undefined): value is Foo;
|
|
454
|
+
{
|
|
455
|
+
type: 'ExportNamedDeclaration',
|
|
456
|
+
declaration: {
|
|
457
|
+
type: 'TSDeclareFunction',
|
|
458
|
+
id: {
|
|
459
|
+
type: 'Identifier',
|
|
460
|
+
name: 'isValid',
|
|
461
|
+
},
|
|
462
|
+
generator: false,
|
|
463
|
+
expression: false,
|
|
464
|
+
async: false,
|
|
465
|
+
params: [
|
|
466
|
+
{
|
|
467
|
+
type: 'Identifier',
|
|
468
|
+
name: 'value',
|
|
469
|
+
typeAnnotation: {
|
|
470
|
+
type: 'TSTypeAnnotation',
|
|
471
|
+
typeAnnotation: {
|
|
472
|
+
type: 'TSUnionType',
|
|
473
|
+
types: [
|
|
474
|
+
bodyRepresentationType,
|
|
475
|
+
{
|
|
476
|
+
type: 'TSNullKeyword',
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
type: 'TSUndefinedKeyword',
|
|
480
|
+
},
|
|
481
|
+
],
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
returnType: {
|
|
487
|
+
type: 'TSTypeAnnotation',
|
|
488
|
+
typeAnnotation: {
|
|
489
|
+
type: 'TSTypePredicate',
|
|
490
|
+
asserts: false,
|
|
491
|
+
parameterName: {
|
|
492
|
+
type: 'Identifier',
|
|
493
|
+
name: 'value',
|
|
494
|
+
},
|
|
495
|
+
typeAnnotation: {
|
|
496
|
+
type: 'TSTypeAnnotation',
|
|
497
|
+
typeAnnotation: {
|
|
498
|
+
type: 'TSTypeReference',
|
|
499
|
+
typeName: enumName,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
},
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
specifiers: [],
|
|
506
|
+
source: null,
|
|
507
|
+
exportKind: 'value',
|
|
508
|
+
assertions: [],
|
|
509
|
+
},
|
|
510
|
+
// export function members(): IterableIterator<Foo>;
|
|
511
|
+
{
|
|
512
|
+
type: 'ExportNamedDeclaration',
|
|
513
|
+
declaration: {
|
|
514
|
+
type: 'TSDeclareFunction',
|
|
515
|
+
id: {
|
|
516
|
+
type: 'Identifier',
|
|
517
|
+
name: 'members',
|
|
518
|
+
},
|
|
519
|
+
generator: false,
|
|
520
|
+
expression: false,
|
|
521
|
+
async: false,
|
|
522
|
+
params: [],
|
|
523
|
+
returnType: {
|
|
524
|
+
type: 'TSTypeAnnotation',
|
|
525
|
+
typeAnnotation: {
|
|
526
|
+
type: 'TSTypeReference',
|
|
527
|
+
typeName: {
|
|
528
|
+
type: 'Identifier',
|
|
529
|
+
name: 'IterableIterator',
|
|
530
|
+
},
|
|
531
|
+
typeParameters: {
|
|
532
|
+
type: 'TSTypeParameterInstantiation',
|
|
533
|
+
params: [
|
|
534
|
+
{
|
|
535
|
+
type: 'TSTypeReference',
|
|
536
|
+
typeName: enumName,
|
|
537
|
+
},
|
|
538
|
+
],
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
specifiers: [],
|
|
544
|
+
source: null,
|
|
545
|
+
exportKind: 'value',
|
|
546
|
+
assertions: [],
|
|
547
|
+
},
|
|
548
|
+
// export function getName(value: Foo): string;
|
|
549
|
+
{
|
|
550
|
+
type: 'ExportNamedDeclaration',
|
|
551
|
+
declaration: {
|
|
552
|
+
type: 'TSDeclareFunction',
|
|
553
|
+
id: {
|
|
554
|
+
type: 'Identifier',
|
|
555
|
+
name: 'getName',
|
|
556
|
+
},
|
|
557
|
+
generator: false,
|
|
558
|
+
expression: false,
|
|
559
|
+
async: false,
|
|
560
|
+
params: [
|
|
561
|
+
{
|
|
562
|
+
type: 'Identifier',
|
|
563
|
+
name: 'value',
|
|
564
|
+
typeAnnotation: {
|
|
565
|
+
type: 'TSTypeAnnotation',
|
|
566
|
+
typeAnnotation: {
|
|
567
|
+
type: 'TSTypeReference',
|
|
568
|
+
typeName: enumName,
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
returnType: {
|
|
574
|
+
type: 'TSTypeAnnotation',
|
|
575
|
+
typeAnnotation: {
|
|
576
|
+
type: 'TSStringKeyword',
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
specifiers: [],
|
|
581
|
+
source: null,
|
|
582
|
+
exportKind: 'value',
|
|
583
|
+
assertions: [],
|
|
584
|
+
},
|
|
585
|
+
],
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const getPlaceholderNameForTypeofImport: () => string = (() => {
|
|
592
|
+
let typeof_import_count = 0;
|
|
593
|
+
return () => `$$IMPORT_TYPEOF_${++typeof_import_count}$$`;
|
|
594
|
+
})();
|
|
595
|
+
|
|
596
|
+
const transformTypeAnnotationType = (
|
|
597
|
+
node: FlowESTree.TypeAnnotationType,
|
|
598
|
+
): TSESTree.TypeNode => {
|
|
599
|
+
switch (node.type) {
|
|
600
|
+
case 'AnyTypeAnnotation':
|
|
601
|
+
return transform.AnyTypeAnnotation(node);
|
|
602
|
+
case 'ArrayTypeAnnotation':
|
|
603
|
+
return transform.ArrayTypeAnnotation(node);
|
|
604
|
+
case 'BigIntLiteralTypeAnnotation':
|
|
605
|
+
return transform.BigIntLiteralTypeAnnotation(node);
|
|
606
|
+
case 'BigIntTypeAnnotation':
|
|
607
|
+
return transform.BigIntTypeAnnotation(node);
|
|
608
|
+
case 'BooleanLiteralTypeAnnotation':
|
|
609
|
+
return transform.BooleanLiteralTypeAnnotation(node);
|
|
610
|
+
case 'BooleanTypeAnnotation':
|
|
611
|
+
return transform.BooleanTypeAnnotation(node);
|
|
612
|
+
case 'EmptyTypeAnnotation':
|
|
613
|
+
return transform.EmptyTypeAnnotation(node);
|
|
614
|
+
case 'ExistsTypeAnnotation':
|
|
615
|
+
return transform.ExistsTypeAnnotation(node);
|
|
616
|
+
case 'FunctionTypeAnnotation':
|
|
617
|
+
return transform.FunctionTypeAnnotation(node);
|
|
618
|
+
case 'GenericTypeAnnotation':
|
|
619
|
+
return transform.GenericTypeAnnotation(node);
|
|
620
|
+
case 'IndexedAccessType':
|
|
621
|
+
return transform.IndexedAccessType(node);
|
|
622
|
+
case 'InterfaceTypeAnnotation':
|
|
623
|
+
return transform.InterfaceTypeAnnotation(node);
|
|
624
|
+
case 'IntersectionTypeAnnotation':
|
|
625
|
+
return transform.IntersectionTypeAnnotation(node);
|
|
626
|
+
case 'MixedTypeAnnotation':
|
|
627
|
+
return transform.MixedTypeAnnotation(node);
|
|
628
|
+
case 'NullLiteralTypeAnnotation':
|
|
629
|
+
return transform.NullLiteralTypeAnnotation(node);
|
|
630
|
+
case 'NullableTypeAnnotation':
|
|
631
|
+
return transform.NullableTypeAnnotation(node);
|
|
632
|
+
case 'NumberLiteralTypeAnnotation':
|
|
633
|
+
return transform.NumberLiteralTypeAnnotation(node);
|
|
634
|
+
case 'NumberTypeAnnotation':
|
|
635
|
+
return transform.NumberTypeAnnotation(node);
|
|
636
|
+
case 'ObjectTypeAnnotation':
|
|
637
|
+
return transform.ObjectTypeAnnotation(node);
|
|
638
|
+
case 'OptionalIndexedAccessType':
|
|
639
|
+
return transform.OptionalIndexedAccessType(node);
|
|
640
|
+
case 'QualifiedTypeIdentifier':
|
|
641
|
+
return transform.QualifiedTypeIdentifier(node);
|
|
642
|
+
case 'StringLiteralTypeAnnotation':
|
|
643
|
+
return transform.StringLiteralTypeAnnotation(node);
|
|
644
|
+
case 'StringTypeAnnotation':
|
|
645
|
+
return transform.StringTypeAnnotation(node);
|
|
646
|
+
case 'SymbolTypeAnnotation':
|
|
647
|
+
return transform.SymbolTypeAnnotation(node);
|
|
648
|
+
case 'ThisTypeAnnotation':
|
|
649
|
+
return transform.ThisTypeAnnotation(node);
|
|
650
|
+
case 'TupleTypeAnnotation':
|
|
651
|
+
return transform.TupleTypeAnnotation(node);
|
|
652
|
+
case 'TupleTypeLabeledElement':
|
|
653
|
+
case 'TupleTypeSpreadElement':
|
|
654
|
+
return unsupportedAnnotation(node, node.type);
|
|
655
|
+
case 'TypeofTypeAnnotation':
|
|
656
|
+
return transform.TypeofTypeAnnotation(node);
|
|
657
|
+
case 'UnionTypeAnnotation':
|
|
658
|
+
return transform.UnionTypeAnnotation(node);
|
|
659
|
+
case 'VoidTypeAnnotation':
|
|
660
|
+
return transform.VoidTypeAnnotation(node);
|
|
661
|
+
case 'TypePredicate':
|
|
662
|
+
return unsupportedAnnotation(node, node.type);
|
|
663
|
+
default:
|
|
664
|
+
throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
|
|
141
668
|
const transform = {
|
|
142
669
|
AnyTypeAnnotation(
|
|
143
670
|
_node: FlowESTree.AnyTypeAnnotation,
|
|
@@ -151,7 +678,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
151
678
|
): TSESTree.TSArrayType {
|
|
152
679
|
return {
|
|
153
680
|
type: 'TSArrayType',
|
|
154
|
-
elementType:
|
|
681
|
+
elementType: transformTypeAnnotationType(node.elementType),
|
|
155
682
|
};
|
|
156
683
|
},
|
|
157
684
|
BigIntLiteral(node: FlowESTree.BigIntLiteral): TSESTree.BigIntLiteral {
|
|
@@ -233,13 +760,16 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
233
760
|
},
|
|
234
761
|
DeclareClass(
|
|
235
762
|
node: FlowESTree.DeclareClass,
|
|
236
|
-
): TSESTree.ClassDeclarationWithName {
|
|
763
|
+
): DeclarationOrUnsupported<TSESTree.ClassDeclarationWithName> {
|
|
237
764
|
const classMembers: Array<TSESTree.ClassElement> = [];
|
|
238
765
|
const transformedBody = transform.ObjectTypeAnnotation(node.body);
|
|
239
766
|
if (transformedBody.type !== 'TSTypeLiteral') {
|
|
240
|
-
|
|
767
|
+
return unsupportedDeclaration(
|
|
241
768
|
node.body,
|
|
242
769
|
'Spreads in declare class are not allowed',
|
|
770
|
+
node.id,
|
|
771
|
+
true,
|
|
772
|
+
node.typeParameters,
|
|
243
773
|
);
|
|
244
774
|
}
|
|
245
775
|
for (const member of transformedBody.members) {
|
|
@@ -359,9 +889,12 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
359
889
|
```
|
|
360
890
|
Let's put a pin in it for now and deal with it later if the need arises.
|
|
361
891
|
*/
|
|
362
|
-
|
|
892
|
+
return unsupportedDeclaration(
|
|
363
893
|
node.body.callProperties[0] ?? node.body,
|
|
364
894
|
'call signatures on classes',
|
|
895
|
+
node.id,
|
|
896
|
+
true,
|
|
897
|
+
node.typeParameters,
|
|
365
898
|
);
|
|
366
899
|
}
|
|
367
900
|
|
|
@@ -406,12 +939,14 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
406
939
|
node: FlowESTree.DeclareExportDeclaration,
|
|
407
940
|
):
|
|
408
941
|
| TSESTree.ExportNamedDeclaration
|
|
942
|
+
| Array<TSESTree.ExportNamedDeclaration>
|
|
409
943
|
| TSESTree.ExportDefaultDeclaration
|
|
410
944
|
| [
|
|
411
945
|
(
|
|
412
946
|
| TSESTree.VariableDeclaration
|
|
413
947
|
| TSESTree.ClassDeclaration
|
|
414
948
|
| TSESTree.TSDeclareFunction
|
|
949
|
+
| TSESTree.TSTypeAliasDeclaration
|
|
415
950
|
),
|
|
416
951
|
TSESTree.ExportDefaultDeclaration,
|
|
417
952
|
] {
|
|
@@ -466,56 +1001,51 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
466
1001
|
// only Identifiers can be handled here.
|
|
467
1002
|
if (referencedId.type === 'Identifier') {
|
|
468
1003
|
const exportedVar = topScope.set.get(referencedId.name);
|
|
469
|
-
if (exportedVar
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1004
|
+
if (exportedVar != null && exportedVar.defs.length === 1) {
|
|
1005
|
+
const def = exportedVar.defs[0];
|
|
1006
|
+
switch (def.type) {
|
|
1007
|
+
case 'ImportBinding': {
|
|
1008
|
+
// `import type { Wut } from 'mod'; declare export default Wut;`
|
|
1009
|
+
// `import { type Wut } from 'mod'; declare export default Wut;`
|
|
1010
|
+
// these cases should be wrapped in a variable because they're exporting a type, not a value
|
|
1011
|
+
const specifier = def.node;
|
|
1012
|
+
if (
|
|
1013
|
+
specifier.importKind === 'type' ||
|
|
1014
|
+
specifier.parent.importKind === 'type'
|
|
1015
|
+
) {
|
|
1016
|
+
// fallthrough to the "default" handling
|
|
1017
|
+
break;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// intentional fallthrough to the "value" handling
|
|
1021
|
+
}
|
|
1022
|
+
case 'ClassName':
|
|
1023
|
+
case 'Enum':
|
|
1024
|
+
case 'FunctionName':
|
|
1025
|
+
case 'ImplicitGlobalVariable':
|
|
1026
|
+
case 'Variable':
|
|
1027
|
+
// there's already a variable defined to hold the type
|
|
1028
|
+
return {
|
|
1029
|
+
type: 'ExportDefaultDeclaration',
|
|
1030
|
+
declaration: {
|
|
1031
|
+
type: 'Identifier',
|
|
1032
|
+
name: referencedId.name,
|
|
1033
|
+
},
|
|
1034
|
+
exportKind: 'value',
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
case 'CatchClause':
|
|
1038
|
+
case 'Parameter':
|
|
1039
|
+
case 'TypeParameter':
|
|
1040
|
+
throw translationError(
|
|
1041
|
+
def.node,
|
|
1042
|
+
`Unexpected variable def type: ${def.type}`,
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
case 'Type':
|
|
487
1046
|
// fallthrough to the "default" handling
|
|
488
1047
|
break;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// intentional fallthrough to the "value" handling
|
|
492
1048
|
}
|
|
493
|
-
case 'ClassName':
|
|
494
|
-
case 'Enum':
|
|
495
|
-
case 'FunctionName':
|
|
496
|
-
case 'ImplicitGlobalVariable':
|
|
497
|
-
case 'Variable':
|
|
498
|
-
// there's already a variable defined to hold the type
|
|
499
|
-
return {
|
|
500
|
-
type: 'ExportDefaultDeclaration',
|
|
501
|
-
declaration: {
|
|
502
|
-
type: 'Identifier',
|
|
503
|
-
name: referencedId.name,
|
|
504
|
-
},
|
|
505
|
-
exportKind: 'value',
|
|
506
|
-
};
|
|
507
|
-
|
|
508
|
-
case 'CatchClause':
|
|
509
|
-
case 'Parameter':
|
|
510
|
-
case 'TypeParameter':
|
|
511
|
-
throw translationError(
|
|
512
|
-
def.node,
|
|
513
|
-
`Unexpected variable def type: ${def.type}`,
|
|
514
|
-
);
|
|
515
|
-
|
|
516
|
-
case 'Type':
|
|
517
|
-
// fallthrough to the "default" handling
|
|
518
|
-
break;
|
|
519
1049
|
}
|
|
520
1050
|
}
|
|
521
1051
|
|
|
@@ -548,7 +1078,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
548
1078
|
typeAnnotation: {
|
|
549
1079
|
type: 'TSTypeAnnotation',
|
|
550
1080
|
typeAnnotation:
|
|
551
|
-
|
|
1081
|
+
transformTypeAnnotationType(declaration),
|
|
552
1082
|
},
|
|
553
1083
|
},
|
|
554
1084
|
init: null,
|
|
@@ -585,45 +1115,73 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
585
1115
|
}: TSESTree.ExportNamedDeclarationWithoutSourceWithMultiple);
|
|
586
1116
|
}
|
|
587
1117
|
|
|
588
|
-
const
|
|
1118
|
+
const declarations = (() => {
|
|
589
1119
|
switch (node.declaration.type) {
|
|
590
1120
|
case 'DeclareClass':
|
|
591
|
-
return
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
1121
|
+
return [
|
|
1122
|
+
{
|
|
1123
|
+
declaration: transform.DeclareClass(node.declaration),
|
|
1124
|
+
exportKind: 'value',
|
|
1125
|
+
},
|
|
1126
|
+
];
|
|
595
1127
|
case 'DeclareFunction':
|
|
596
|
-
return
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
1128
|
+
return [
|
|
1129
|
+
{
|
|
1130
|
+
declaration: transform.DeclareFunction(node.declaration),
|
|
1131
|
+
exportKind: 'value',
|
|
1132
|
+
},
|
|
1133
|
+
];
|
|
600
1134
|
case 'DeclareInterface':
|
|
601
|
-
return
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
1135
|
+
return [
|
|
1136
|
+
{
|
|
1137
|
+
declaration: transform.DeclareInterface(node.declaration),
|
|
1138
|
+
exportKind: 'type',
|
|
1139
|
+
},
|
|
1140
|
+
];
|
|
605
1141
|
case 'DeclareOpaqueType':
|
|
606
|
-
return
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
1142
|
+
return [
|
|
1143
|
+
{
|
|
1144
|
+
declaration: transform.DeclareOpaqueType(node.declaration),
|
|
1145
|
+
exportKind: 'type',
|
|
1146
|
+
},
|
|
1147
|
+
];
|
|
610
1148
|
case 'DeclareVariable':
|
|
611
|
-
return
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
1149
|
+
return [
|
|
1150
|
+
{
|
|
1151
|
+
declaration: transform.DeclareVariable(node.declaration),
|
|
1152
|
+
exportKind: 'value',
|
|
1153
|
+
},
|
|
1154
|
+
];
|
|
1155
|
+
case 'DeclareEnum': {
|
|
1156
|
+
const result = transform.DeclareEnum(node.declaration);
|
|
1157
|
+
return Array.isArray(result)
|
|
1158
|
+
? [
|
|
1159
|
+
{
|
|
1160
|
+
declaration: result[0],
|
|
1161
|
+
exportKind: 'type',
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
declaration: result[1],
|
|
1165
|
+
exportKind: 'type',
|
|
1166
|
+
},
|
|
1167
|
+
]
|
|
1168
|
+
: [{declaration: result, exportKind: 'type'}];
|
|
1169
|
+
}
|
|
615
1170
|
}
|
|
616
1171
|
})();
|
|
617
1172
|
|
|
618
|
-
return (
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1173
|
+
return declarations.map(
|
|
1174
|
+
({declaration, exportKind}) =>
|
|
1175
|
+
({
|
|
1176
|
+
type: 'ExportNamedDeclaration',
|
|
1177
|
+
// flow does not currently support assertions
|
|
1178
|
+
assertions: [],
|
|
1179
|
+
declaration,
|
|
1180
|
+
exportKind,
|
|
1181
|
+
source: null,
|
|
1182
|
+
specifiers: [],
|
|
1183
|
+
}: TSESTree.ExportNamedDeclarationWithoutSourceWithSingle),
|
|
1184
|
+
);
|
|
627
1185
|
} else {
|
|
628
1186
|
return ({
|
|
629
1187
|
type: 'ExportNamedDeclaration',
|
|
@@ -696,7 +1254,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
696
1254
|
type: 'TSTypeAliasDeclaration',
|
|
697
1255
|
declare: node.type === 'DeclareTypeAlias',
|
|
698
1256
|
id: transform.Identifier(node.id, false),
|
|
699
|
-
typeAnnotation:
|
|
1257
|
+
typeAnnotation: transformTypeAnnotationType(node.right),
|
|
700
1258
|
typeParameters:
|
|
701
1259
|
node.typeParameters == null
|
|
702
1260
|
? undefined
|
|
@@ -719,7 +1277,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
719
1277
|
? {
|
|
720
1278
|
type: 'TSUnknownKeyword',
|
|
721
1279
|
}
|
|
722
|
-
:
|
|
1280
|
+
: transformTypeAnnotationType(node.supertype),
|
|
723
1281
|
typeParameters:
|
|
724
1282
|
node.typeParameters == null
|
|
725
1283
|
? undefined
|
|
@@ -740,9 +1298,16 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
740
1298
|
init: null,
|
|
741
1299
|
},
|
|
742
1300
|
],
|
|
743
|
-
kind:
|
|
1301
|
+
kind: node.kind,
|
|
744
1302
|
};
|
|
745
1303
|
},
|
|
1304
|
+
DeclareEnum(
|
|
1305
|
+
node: FlowESTree.DeclareEnum,
|
|
1306
|
+
): DeclarationOrUnsupported<
|
|
1307
|
+
[TSESTree.TSEnumDeclaration, TSESTree.TSModuleDeclaration],
|
|
1308
|
+
> {
|
|
1309
|
+
return EnumImpl(node);
|
|
1310
|
+
},
|
|
746
1311
|
EmptyTypeAnnotation(
|
|
747
1312
|
node: FlowESTree.EmptyTypeAnnotation,
|
|
748
1313
|
): TSESTree.TypeNode {
|
|
@@ -750,318 +1315,14 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
750
1315
|
// The closest is `never`, but `never` has a number of different semantics
|
|
751
1316
|
// In reality no human code should ever directly use the `empty` type in flow
|
|
752
1317
|
// So let's put a pin in it for now
|
|
753
|
-
|
|
754
|
-
},
|
|
755
|
-
EnumDeclaration(
|
|
756
|
-
node: FlowESTree.EnumDeclaration,
|
|
757
|
-
):
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
There's unfortunately no way for us to support this in a clean way.
|
|
762
|
-
We can get really close using this code:
|
|
763
|
-
```
|
|
764
|
-
declare namespace SymbolEnum {
|
|
765
|
-
export const member1: unique symbol;
|
|
766
|
-
export type member1 = typeof member1;
|
|
767
|
-
|
|
768
|
-
export const member2: unique symbol;
|
|
769
|
-
export type member2 = typeof member2;
|
|
770
|
-
}
|
|
771
|
-
type SymbolEnum = typeof SymbolEnum[keyof typeof SymbolEnum];
|
|
772
|
-
```
|
|
773
|
-
|
|
774
|
-
However as explained in https://github.com/microsoft/TypeScript/issues/43657:
|
|
775
|
-
"A unique symbol type is never transferred from one declaration to another through inference."
|
|
776
|
-
This intended behaviour in TS means that the usage of the fake-enum would look like this:
|
|
777
|
-
```
|
|
778
|
-
const value: SymbolEnum.member1 = SymbolEnum.member1;
|
|
779
|
-
// ^^^^^^^^^^^^^^^^^^ required to force TS to retain the information
|
|
780
|
-
```
|
|
781
|
-
Which is really clunky and shitty. It definitely works, but ofc it's not good.
|
|
782
|
-
We can go with this design if users are okay with it!
|
|
783
|
-
|
|
784
|
-
Considering how rarely used symbol enums are ATM, let's just put a pin in it for now.
|
|
785
|
-
*/
|
|
786
|
-
throw unsupportedTranslationError(node, 'symbol enums');
|
|
787
|
-
}
|
|
788
|
-
if (body.type === 'EnumBooleanBody') {
|
|
789
|
-
/*
|
|
790
|
-
TODO - TS enums only allow strings or numbers as their values - not booleans.
|
|
791
|
-
This means we need a non-ts-enum representation of the enum.
|
|
792
|
-
We can support boolean enums using a construct like this:
|
|
793
|
-
```ts
|
|
794
|
-
declare namespace BooleanEnum {
|
|
795
|
-
export const member1: true;
|
|
796
|
-
export type member1 = typeof member1;
|
|
797
|
-
|
|
798
|
-
export const member2: false;
|
|
799
|
-
export type member2 = typeof member1;
|
|
800
|
-
}
|
|
801
|
-
declare type BooleanEnum = boolean;
|
|
802
|
-
```
|
|
803
|
-
|
|
804
|
-
But it's pretty clunky and ugly.
|
|
805
|
-
Considering how rarely used boolean enums are ATM, let's just put a pin in it for now.
|
|
806
|
-
*/
|
|
807
|
-
throw unsupportedTranslationError(node, 'boolean enums');
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
const members: Array<TSESTree.TSEnumMemberNonComputedName> = [];
|
|
811
|
-
for (const member of body.members) {
|
|
812
|
-
switch (member.type) {
|
|
813
|
-
case 'EnumDefaultedMember': {
|
|
814
|
-
if (body.type === 'EnumNumberBody') {
|
|
815
|
-
// this should be impossible!
|
|
816
|
-
throw unexpectedTranslationError(
|
|
817
|
-
member,
|
|
818
|
-
'Unexpected defaulted number enum member',
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
members.push({
|
|
822
|
-
type: 'TSEnumMember',
|
|
823
|
-
computed: false,
|
|
824
|
-
id: transform.Identifier(member.id, false),
|
|
825
|
-
initializer: ({
|
|
826
|
-
type: 'Literal',
|
|
827
|
-
raw: `"${member.id.name}"`,
|
|
828
|
-
value: member.id.name,
|
|
829
|
-
}: TSESTree.StringLiteral),
|
|
830
|
-
});
|
|
831
|
-
break;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
case 'EnumNumberMember':
|
|
835
|
-
case 'EnumStringMember':
|
|
836
|
-
members.push({
|
|
837
|
-
type: 'TSEnumMember',
|
|
838
|
-
computed: false,
|
|
839
|
-
id: transform.Identifier(member.id, false),
|
|
840
|
-
initializer:
|
|
841
|
-
member.init.literalType === 'string'
|
|
842
|
-
? transform.StringLiteral(member.init)
|
|
843
|
-
: transform.NumericLiteral(member.init),
|
|
844
|
-
});
|
|
845
|
-
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
const bodyRepresentationType =
|
|
849
|
-
body.type === 'EnumNumberBody'
|
|
850
|
-
? {type: 'TSNumberKeyword'}
|
|
851
|
-
: {type: 'TSStringKeyword'};
|
|
852
|
-
|
|
853
|
-
const enumName = transform.Identifier(node.id, false);
|
|
854
|
-
return [
|
|
855
|
-
{
|
|
856
|
-
type: 'TSEnumDeclaration',
|
|
857
|
-
const: false,
|
|
858
|
-
declare: true,
|
|
859
|
-
id: enumName,
|
|
860
|
-
members,
|
|
861
|
-
},
|
|
862
|
-
// flow also exports `.cast`, `.isValid`, `.members` and `.getName` for enums
|
|
863
|
-
// we can use declaration merging to declare these functions on the enum:
|
|
864
|
-
/*
|
|
865
|
-
declare enum Foo {
|
|
866
|
-
A = 1,
|
|
867
|
-
B = 2,
|
|
868
|
-
}
|
|
869
|
-
declare namespace Foo {
|
|
870
|
-
export function cast(value: number | null | undefined): Foo;
|
|
871
|
-
export function isValid(value: number | null | undefined): value is Foo;
|
|
872
|
-
export function members(): IterableIterator<Foo>;
|
|
873
|
-
export function getName(value: Foo): string;
|
|
874
|
-
}
|
|
875
|
-
*/
|
|
876
|
-
{
|
|
877
|
-
type: 'TSModuleDeclaration',
|
|
878
|
-
declare: true,
|
|
879
|
-
id: enumName,
|
|
880
|
-
body: {
|
|
881
|
-
type: 'TSModuleBlock',
|
|
882
|
-
body: [
|
|
883
|
-
// export function cast(value: number | null | undefined): Foo
|
|
884
|
-
{
|
|
885
|
-
type: 'ExportNamedDeclaration',
|
|
886
|
-
declaration: {
|
|
887
|
-
type: 'TSDeclareFunction',
|
|
888
|
-
id: {
|
|
889
|
-
type: 'Identifier',
|
|
890
|
-
name: 'cast',
|
|
891
|
-
},
|
|
892
|
-
generator: false,
|
|
893
|
-
expression: false,
|
|
894
|
-
async: false,
|
|
895
|
-
params: [
|
|
896
|
-
{
|
|
897
|
-
type: 'Identifier',
|
|
898
|
-
name: 'value',
|
|
899
|
-
typeAnnotation: {
|
|
900
|
-
type: 'TSTypeAnnotation',
|
|
901
|
-
typeAnnotation: {
|
|
902
|
-
type: 'TSUnionType',
|
|
903
|
-
types: [
|
|
904
|
-
bodyRepresentationType,
|
|
905
|
-
{
|
|
906
|
-
type: 'TSNullKeyword',
|
|
907
|
-
},
|
|
908
|
-
{
|
|
909
|
-
type: 'TSUndefinedKeyword',
|
|
910
|
-
},
|
|
911
|
-
],
|
|
912
|
-
},
|
|
913
|
-
},
|
|
914
|
-
},
|
|
915
|
-
],
|
|
916
|
-
returnType: {
|
|
917
|
-
type: 'TSTypeAnnotation',
|
|
918
|
-
typeAnnotation: {
|
|
919
|
-
type: 'TSTypeReference',
|
|
920
|
-
typeName: enumName,
|
|
921
|
-
},
|
|
922
|
-
},
|
|
923
|
-
},
|
|
924
|
-
specifiers: [],
|
|
925
|
-
source: null,
|
|
926
|
-
exportKind: 'value',
|
|
927
|
-
assertions: [],
|
|
928
|
-
},
|
|
929
|
-
// export function isValid(value: number | null | undefined): value is Foo;
|
|
930
|
-
{
|
|
931
|
-
type: 'ExportNamedDeclaration',
|
|
932
|
-
declaration: {
|
|
933
|
-
type: 'TSDeclareFunction',
|
|
934
|
-
id: {
|
|
935
|
-
type: 'Identifier',
|
|
936
|
-
name: 'isValid',
|
|
937
|
-
},
|
|
938
|
-
generator: false,
|
|
939
|
-
expression: false,
|
|
940
|
-
async: false,
|
|
941
|
-
params: [
|
|
942
|
-
{
|
|
943
|
-
type: 'Identifier',
|
|
944
|
-
name: 'value',
|
|
945
|
-
typeAnnotation: {
|
|
946
|
-
type: 'TSTypeAnnotation',
|
|
947
|
-
typeAnnotation: {
|
|
948
|
-
type: 'TSUnionType',
|
|
949
|
-
types: [
|
|
950
|
-
bodyRepresentationType,
|
|
951
|
-
{
|
|
952
|
-
type: 'TSNullKeyword',
|
|
953
|
-
},
|
|
954
|
-
{
|
|
955
|
-
type: 'TSUndefinedKeyword',
|
|
956
|
-
},
|
|
957
|
-
],
|
|
958
|
-
},
|
|
959
|
-
},
|
|
960
|
-
},
|
|
961
|
-
],
|
|
962
|
-
returnType: {
|
|
963
|
-
type: 'TSTypeAnnotation',
|
|
964
|
-
typeAnnotation: {
|
|
965
|
-
type: 'TSTypePredicate',
|
|
966
|
-
asserts: false,
|
|
967
|
-
parameterName: {
|
|
968
|
-
type: 'Identifier',
|
|
969
|
-
name: 'value',
|
|
970
|
-
},
|
|
971
|
-
typeAnnotation: {
|
|
972
|
-
type: 'TSTypeAnnotation',
|
|
973
|
-
typeAnnotation: {
|
|
974
|
-
type: 'TSTypeReference',
|
|
975
|
-
typeName: enumName,
|
|
976
|
-
},
|
|
977
|
-
},
|
|
978
|
-
},
|
|
979
|
-
},
|
|
980
|
-
},
|
|
981
|
-
specifiers: [],
|
|
982
|
-
source: null,
|
|
983
|
-
exportKind: 'value',
|
|
984
|
-
assertions: [],
|
|
985
|
-
},
|
|
986
|
-
// export function members(): IterableIterator<Foo>;
|
|
987
|
-
{
|
|
988
|
-
type: 'ExportNamedDeclaration',
|
|
989
|
-
declaration: {
|
|
990
|
-
type: 'TSDeclareFunction',
|
|
991
|
-
id: {
|
|
992
|
-
type: 'Identifier',
|
|
993
|
-
name: 'members',
|
|
994
|
-
},
|
|
995
|
-
generator: false,
|
|
996
|
-
expression: false,
|
|
997
|
-
async: false,
|
|
998
|
-
params: [],
|
|
999
|
-
returnType: {
|
|
1000
|
-
type: 'TSTypeAnnotation',
|
|
1001
|
-
typeAnnotation: {
|
|
1002
|
-
type: 'TSTypeReference',
|
|
1003
|
-
typeName: {
|
|
1004
|
-
type: 'Identifier',
|
|
1005
|
-
name: 'IterableIterator',
|
|
1006
|
-
},
|
|
1007
|
-
typeParameters: {
|
|
1008
|
-
type: 'TSTypeParameterInstantiation',
|
|
1009
|
-
params: [
|
|
1010
|
-
{
|
|
1011
|
-
type: 'TSTypeReference',
|
|
1012
|
-
typeName: enumName,
|
|
1013
|
-
},
|
|
1014
|
-
],
|
|
1015
|
-
},
|
|
1016
|
-
},
|
|
1017
|
-
},
|
|
1018
|
-
},
|
|
1019
|
-
specifiers: [],
|
|
1020
|
-
source: null,
|
|
1021
|
-
exportKind: 'value',
|
|
1022
|
-
assertions: [],
|
|
1023
|
-
},
|
|
1024
|
-
// export function getName(value: Foo): string;
|
|
1025
|
-
{
|
|
1026
|
-
type: 'ExportNamedDeclaration',
|
|
1027
|
-
declaration: {
|
|
1028
|
-
type: 'TSDeclareFunction',
|
|
1029
|
-
id: {
|
|
1030
|
-
type: 'Identifier',
|
|
1031
|
-
name: 'getName',
|
|
1032
|
-
},
|
|
1033
|
-
generator: false,
|
|
1034
|
-
expression: false,
|
|
1035
|
-
async: false,
|
|
1036
|
-
params: [
|
|
1037
|
-
{
|
|
1038
|
-
type: 'Identifier',
|
|
1039
|
-
name: 'value',
|
|
1040
|
-
typeAnnotation: {
|
|
1041
|
-
type: 'TSTypeAnnotation',
|
|
1042
|
-
typeAnnotation: {
|
|
1043
|
-
type: 'TSTypeReference',
|
|
1044
|
-
typeName: enumName,
|
|
1045
|
-
},
|
|
1046
|
-
},
|
|
1047
|
-
},
|
|
1048
|
-
],
|
|
1049
|
-
returnType: {
|
|
1050
|
-
type: 'TSTypeAnnotation',
|
|
1051
|
-
typeAnnotation: {
|
|
1052
|
-
type: 'TSStringKeyword',
|
|
1053
|
-
},
|
|
1054
|
-
},
|
|
1055
|
-
},
|
|
1056
|
-
specifiers: [],
|
|
1057
|
-
source: null,
|
|
1058
|
-
exportKind: 'value',
|
|
1059
|
-
assertions: [],
|
|
1060
|
-
},
|
|
1061
|
-
],
|
|
1062
|
-
},
|
|
1063
|
-
},
|
|
1064
|
-
];
|
|
1318
|
+
return unsupportedAnnotation(node, 'empty type');
|
|
1319
|
+
},
|
|
1320
|
+
EnumDeclaration(
|
|
1321
|
+
node: FlowESTree.EnumDeclaration,
|
|
1322
|
+
): DeclarationOrUnsupported<
|
|
1323
|
+
[TSESTree.TSEnumDeclaration, TSESTree.TSModuleDeclaration],
|
|
1324
|
+
> {
|
|
1325
|
+
return EnumImpl(node);
|
|
1065
1326
|
},
|
|
1066
1327
|
DeclareModuleExports(
|
|
1067
1328
|
node: FlowESTree.DeclareModuleExports,
|
|
@@ -1073,7 +1334,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1073
1334
|
): TSESTree.TypeNode {
|
|
1074
1335
|
// The existential type does not map to any types in TS
|
|
1075
1336
|
// It's also super deprecated - so let's not ever worry
|
|
1076
|
-
|
|
1337
|
+
return unsupportedAnnotation(node, 'existential type');
|
|
1077
1338
|
},
|
|
1078
1339
|
ExportNamedDeclaration(
|
|
1079
1340
|
node: FlowESTree.ExportNamedDeclaration,
|
|
@@ -1096,11 +1357,12 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1096
1357
|
|
|
1097
1358
|
const [exportedDeclaration, mergedDeclaration] = (() => {
|
|
1098
1359
|
if (node.declaration == null) {
|
|
1099
|
-
return [null];
|
|
1360
|
+
return [null, null];
|
|
1100
1361
|
}
|
|
1101
1362
|
|
|
1102
1363
|
switch (node.declaration.type) {
|
|
1103
1364
|
case 'ClassDeclaration':
|
|
1365
|
+
case 'ComponentDeclaration':
|
|
1104
1366
|
case 'FunctionDeclaration':
|
|
1105
1367
|
case 'VariableDeclaration':
|
|
1106
1368
|
// These cases shouldn't happen in flow defs because they have their own special
|
|
@@ -1110,8 +1372,10 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1110
1372
|
`Unexpected named declaration found ${node.declaration.type}`,
|
|
1111
1373
|
);
|
|
1112
1374
|
|
|
1113
|
-
case 'EnumDeclaration':
|
|
1114
|
-
|
|
1375
|
+
case 'EnumDeclaration': {
|
|
1376
|
+
const result = transform.EnumDeclaration(node.declaration);
|
|
1377
|
+
return Array.isArray(result) ? result : [result, null];
|
|
1378
|
+
}
|
|
1115
1379
|
case 'InterfaceDeclaration':
|
|
1116
1380
|
return [transform.InterfaceDeclaration(node.declaration), null];
|
|
1117
1381
|
case 'OpaqueType':
|
|
@@ -1168,7 +1432,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1168
1432
|
name: 'this',
|
|
1169
1433
|
typeAnnotation: {
|
|
1170
1434
|
type: 'TSTypeAnnotation',
|
|
1171
|
-
typeAnnotation:
|
|
1435
|
+
typeAnnotation: transformTypeAnnotationType(
|
|
1172
1436
|
node.this.typeAnnotation,
|
|
1173
1437
|
),
|
|
1174
1438
|
},
|
|
@@ -1187,7 +1451,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1187
1451
|
: transform.Identifier(rest.name, false),
|
|
1188
1452
|
typeAnnotation: {
|
|
1189
1453
|
type: 'TSTypeAnnotation',
|
|
1190
|
-
typeAnnotation:
|
|
1454
|
+
typeAnnotation: transformTypeAnnotationType(rest.typeAnnotation),
|
|
1191
1455
|
},
|
|
1192
1456
|
});
|
|
1193
1457
|
}
|
|
@@ -1197,7 +1461,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1197
1461
|
params,
|
|
1198
1462
|
returnType: {
|
|
1199
1463
|
type: 'TSTypeAnnotation',
|
|
1200
|
-
typeAnnotation:
|
|
1464
|
+
typeAnnotation: transformTypeAnnotationType(node.returnType),
|
|
1201
1465
|
},
|
|
1202
1466
|
typeParameters:
|
|
1203
1467
|
node.typeParameters == null
|
|
@@ -1214,7 +1478,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1214
1478
|
name: node.name == null ? `$$PARAM_${idx}$$` : node.name.name,
|
|
1215
1479
|
typeAnnotation: {
|
|
1216
1480
|
type: 'TSTypeAnnotation',
|
|
1217
|
-
typeAnnotation:
|
|
1481
|
+
typeAnnotation: transformTypeAnnotationType(node.typeAnnotation),
|
|
1218
1482
|
},
|
|
1219
1483
|
optional: node.optional,
|
|
1220
1484
|
};
|
|
@@ -1261,7 +1525,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1261
1525
|
|
|
1262
1526
|
const res = [];
|
|
1263
1527
|
for (const param of node.typeParameters.params) {
|
|
1264
|
-
res.push(
|
|
1528
|
+
res.push(transformTypeAnnotationType(param));
|
|
1265
1529
|
}
|
|
1266
1530
|
return res;
|
|
1267
1531
|
}
|
|
@@ -1304,7 +1568,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1304
1568
|
type PropType = ReturnType<ExtractPropType<Obj>>; // number
|
|
1305
1569
|
```
|
|
1306
1570
|
*/
|
|
1307
|
-
|
|
1571
|
+
return unsupportedAnnotation(node, fullTypeName);
|
|
1308
1572
|
}
|
|
1309
1573
|
|
|
1310
1574
|
case '$Diff':
|
|
@@ -1498,13 +1762,6 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1498
1762
|
};
|
|
1499
1763
|
}
|
|
1500
1764
|
|
|
1501
|
-
case '$Subtype':
|
|
1502
|
-
case '$Supertype': {
|
|
1503
|
-
// These types are deprecated and shouldn't be used in any modern code
|
|
1504
|
-
// so let's not even bother trying to figure it out
|
|
1505
|
-
throw unsupportedTranslationError(node, fullTypeName);
|
|
1506
|
-
}
|
|
1507
|
-
|
|
1508
1765
|
case '$Values': {
|
|
1509
1766
|
// `$Values<T>` => `T[keyof T]`
|
|
1510
1767
|
const transformedType = assertHasExactlyNTypeParameters(1)[0];
|
|
@@ -1559,16 +1816,148 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1559
1816
|
}
|
|
1560
1817
|
|
|
1561
1818
|
// React special conversion:
|
|
1562
|
-
|
|
1819
|
+
const validReactImportOrGlobal = isValidReactImportOrGlobal(baseId);
|
|
1820
|
+
const reactImport = isReactImport(baseId);
|
|
1821
|
+
if (validReactImportOrGlobal || reactImport) {
|
|
1822
|
+
// Returns appropriate Identifier for `React` import.
|
|
1823
|
+
// If a global is in use, set a flag to indicate that we should add the import.
|
|
1824
|
+
const getReactIdentifier = () => {
|
|
1825
|
+
if (shouldAddReactImport !== false) {
|
|
1826
|
+
shouldAddReactImport = !reactImport;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
return {
|
|
1830
|
+
type: 'Identifier',
|
|
1831
|
+
name: `React`,
|
|
1832
|
+
};
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1563
1835
|
switch (fullTypeName) {
|
|
1836
|
+
// TODO: In flow this is `ChildrenArray<T> = T | $ReadOnlyArray<ChildrenArray<T>>`.
|
|
1837
|
+
// The recursive nature of it is rarely needed, so we're simplifying this for now
|
|
1838
|
+
// but omitting that aspect. Once we're able to provide utility types for our translations,
|
|
1839
|
+
// we should update this.
|
|
1840
|
+
// React.ChildrenArray<T> -> T | ReadonlyArray<T>
|
|
1841
|
+
// React$ChildrenArray<T> -> T | ReadonlyArray<T>
|
|
1842
|
+
case 'React.ChildrenArray':
|
|
1843
|
+
case 'React$ChildrenArray': {
|
|
1844
|
+
const [param] = assertHasExactlyNTypeParameters(1);
|
|
1845
|
+
return {
|
|
1846
|
+
type: 'TSUnionType',
|
|
1847
|
+
types: [
|
|
1848
|
+
param,
|
|
1849
|
+
{
|
|
1850
|
+
type: 'TSTypeReference',
|
|
1851
|
+
typeName: {
|
|
1852
|
+
type: 'Identifier',
|
|
1853
|
+
name: 'ReadonlyArray',
|
|
1854
|
+
},
|
|
1855
|
+
typeParameters: {
|
|
1856
|
+
type: 'TSTypeParameterInstantiation',
|
|
1857
|
+
params: [param],
|
|
1858
|
+
},
|
|
1859
|
+
},
|
|
1860
|
+
],
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
// React.Component<A,B> -> React.Component<A,B>
|
|
1864
|
+
// React$Component<A,B> -> React.Component<A,B>
|
|
1865
|
+
case 'React.Component':
|
|
1866
|
+
case 'React$Component': {
|
|
1867
|
+
const typeParameters = node.typeParameters;
|
|
1868
|
+
if (typeParameters == null || typeParameters.params.length === 0) {
|
|
1869
|
+
throw translationError(
|
|
1870
|
+
node,
|
|
1871
|
+
`Expected at least 1 type parameter with \`${fullTypeName}\``,
|
|
1872
|
+
);
|
|
1873
|
+
}
|
|
1874
|
+
const params = typeParameters.params;
|
|
1875
|
+
if (params.length > 2) {
|
|
1876
|
+
throw translationError(
|
|
1877
|
+
node,
|
|
1878
|
+
`Expected at no more than 2 type parameters with \`${fullTypeName}\``,
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
return {
|
|
1883
|
+
type: 'TSTypeReference',
|
|
1884
|
+
typeName: {
|
|
1885
|
+
type: 'TSQualifiedName',
|
|
1886
|
+
left: getReactIdentifier(),
|
|
1887
|
+
right: {
|
|
1888
|
+
type: 'Identifier',
|
|
1889
|
+
name: 'Component',
|
|
1890
|
+
},
|
|
1891
|
+
},
|
|
1892
|
+
typeParameters: {
|
|
1893
|
+
type: 'TSTypeParameterInstantiation',
|
|
1894
|
+
params: params.map(param => transformTypeAnnotationType(param)),
|
|
1895
|
+
},
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
// React.Context<A> -> React.Context<A>
|
|
1900
|
+
// React$Context<A> -> React.Context<A>
|
|
1901
|
+
case 'React$Context':
|
|
1902
|
+
case 'React.Context':
|
|
1903
|
+
return {
|
|
1904
|
+
type: 'TSTypeReference',
|
|
1905
|
+
typeName: {
|
|
1906
|
+
type: 'TSQualifiedName',
|
|
1907
|
+
left: getReactIdentifier(),
|
|
1908
|
+
right: {
|
|
1909
|
+
type: 'Identifier',
|
|
1910
|
+
name: `Context`,
|
|
1911
|
+
},
|
|
1912
|
+
},
|
|
1913
|
+
typeParameters: {
|
|
1914
|
+
type: 'TSTypeParameterInstantiation',
|
|
1915
|
+
params: assertHasExactlyNTypeParameters(1),
|
|
1916
|
+
},
|
|
1917
|
+
};
|
|
1918
|
+
// React.Key -> React.Key
|
|
1919
|
+
// React$Key -> React.Key
|
|
1920
|
+
case 'React.Key':
|
|
1921
|
+
case 'React$Key':
|
|
1922
|
+
assertHasExactlyNTypeParameters(0);
|
|
1923
|
+
return {
|
|
1924
|
+
type: 'TSTypeReference',
|
|
1925
|
+
typeName: {
|
|
1926
|
+
type: 'TSQualifiedName',
|
|
1927
|
+
left: getReactIdentifier(),
|
|
1928
|
+
right: {
|
|
1929
|
+
type: 'Identifier',
|
|
1930
|
+
name: 'Key',
|
|
1931
|
+
},
|
|
1932
|
+
},
|
|
1933
|
+
};
|
|
1934
|
+
// React.ElementType -> React.ElementType
|
|
1935
|
+
// React$ElementType -> React.ElementType
|
|
1936
|
+
case 'React$ElementType':
|
|
1937
|
+
case 'React.ElementType': {
|
|
1938
|
+
assertHasExactlyNTypeParameters(0);
|
|
1939
|
+
return {
|
|
1940
|
+
type: 'TSTypeReference',
|
|
1941
|
+
typeName: {
|
|
1942
|
+
type: 'TSQualifiedName',
|
|
1943
|
+
left: getReactIdentifier(),
|
|
1944
|
+
right: {
|
|
1945
|
+
type: 'Identifier',
|
|
1946
|
+
name: `ElementType`,
|
|
1947
|
+
},
|
|
1948
|
+
},
|
|
1949
|
+
typeParameters: undefined,
|
|
1950
|
+
};
|
|
1951
|
+
}
|
|
1564
1952
|
// React.Node -> React.ReactNode
|
|
1953
|
+
case 'React$Node':
|
|
1565
1954
|
case 'React.Node': {
|
|
1566
1955
|
assertHasExactlyNTypeParameters(0);
|
|
1567
1956
|
return {
|
|
1568
1957
|
type: 'TSTypeReference',
|
|
1569
1958
|
typeName: {
|
|
1570
1959
|
type: 'TSQualifiedName',
|
|
1571
|
-
left:
|
|
1960
|
+
left: getReactIdentifier(),
|
|
1572
1961
|
right: {
|
|
1573
1962
|
type: 'Identifier',
|
|
1574
1963
|
name: `ReactNode`,
|
|
@@ -1578,12 +1967,13 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1578
1967
|
};
|
|
1579
1968
|
}
|
|
1580
1969
|
// React.Element<typeof Component> -> React.ReactElement<typeof Component>
|
|
1970
|
+
case 'React$Element':
|
|
1581
1971
|
case 'React.Element': {
|
|
1582
1972
|
return {
|
|
1583
1973
|
type: 'TSTypeReference',
|
|
1584
1974
|
typeName: {
|
|
1585
1975
|
type: 'TSQualifiedName',
|
|
1586
|
-
left:
|
|
1976
|
+
left: getReactIdentifier(),
|
|
1587
1977
|
right: {
|
|
1588
1978
|
type: 'Identifier',
|
|
1589
1979
|
name: `ReactElement`,
|
|
@@ -1595,7 +1985,43 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1595
1985
|
},
|
|
1596
1986
|
};
|
|
1597
1987
|
}
|
|
1988
|
+
// React.ElementRef<typeof Component> -> React.ElementRef<typeof Component>
|
|
1989
|
+
// React$ElementRef<typeof Component> -> React.ElementRef<typeof Component>
|
|
1990
|
+
case 'React$ElementRef':
|
|
1991
|
+
case 'React.ElementRef':
|
|
1992
|
+
return {
|
|
1993
|
+
type: 'TSTypeReference',
|
|
1994
|
+
typeName: {
|
|
1995
|
+
type: 'TSQualifiedName',
|
|
1996
|
+
left: getReactIdentifier(),
|
|
1997
|
+
right: {
|
|
1998
|
+
type: 'Identifier',
|
|
1999
|
+
name: `ElementRef`,
|
|
2000
|
+
},
|
|
2001
|
+
},
|
|
2002
|
+
typeParameters: {
|
|
2003
|
+
type: 'TSTypeParameterInstantiation',
|
|
2004
|
+
params: assertHasExactlyNTypeParameters(1),
|
|
2005
|
+
},
|
|
2006
|
+
};
|
|
2007
|
+
// React$Fragment -> React.Fragment
|
|
2008
|
+
// React.Fragment -> React.Fragment
|
|
2009
|
+
case 'React$FragmentType':
|
|
2010
|
+
case 'React.Fragment':
|
|
2011
|
+
assertHasExactlyNTypeParameters(0);
|
|
2012
|
+
return {
|
|
2013
|
+
type: 'TSTypeReference',
|
|
2014
|
+
typeName: {
|
|
2015
|
+
type: 'TSQualifiedName',
|
|
2016
|
+
left: getReactIdentifier(),
|
|
2017
|
+
right: {
|
|
2018
|
+
type: 'Identifier',
|
|
2019
|
+
name: `Fragment`,
|
|
2020
|
+
},
|
|
2021
|
+
},
|
|
2022
|
+
};
|
|
1598
2023
|
// React.MixedElement -> JSX.Element
|
|
2024
|
+
case 'React$MixedElement':
|
|
1599
2025
|
case 'React.MixedElement': {
|
|
1600
2026
|
assertHasExactlyNTypeParameters(0);
|
|
1601
2027
|
return {
|
|
@@ -1614,9 +2040,32 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1614
2040
|
typeParameters: undefined,
|
|
1615
2041
|
};
|
|
1616
2042
|
}
|
|
1617
|
-
// React.
|
|
1618
|
-
// React
|
|
1619
|
-
case 'React.
|
|
2043
|
+
// React.ComponentType<Config> -> React.ComponentType<Config>
|
|
2044
|
+
// React$ComponentType<Config> -> React.ComponentType<Config>
|
|
2045
|
+
case 'React.ComponentType':
|
|
2046
|
+
case 'React$ComponentType': {
|
|
2047
|
+
return {
|
|
2048
|
+
type: 'TSTypeReference',
|
|
2049
|
+
typeName: {
|
|
2050
|
+
type: 'TSQualifiedName',
|
|
2051
|
+
left: getReactIdentifier(),
|
|
2052
|
+
right: {
|
|
2053
|
+
type: 'Identifier',
|
|
2054
|
+
name: 'ComponentType',
|
|
2055
|
+
},
|
|
2056
|
+
},
|
|
2057
|
+
typeParameters: {
|
|
2058
|
+
type: 'TSTypeParameterInstantiation',
|
|
2059
|
+
params: assertHasExactlyNTypeParameters(1),
|
|
2060
|
+
},
|
|
2061
|
+
};
|
|
2062
|
+
}
|
|
2063
|
+
// React.AbstractComponent<Config> -> React.ComponentType<Config>
|
|
2064
|
+
// React$AbstractComponent<Config> -> React.ComponentType<Config>
|
|
2065
|
+
// React.AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
|
|
2066
|
+
// React$AbstractComponent<Config, Instance> -> React.ComponentType<Config & React.RefAttributes<Instance>>
|
|
2067
|
+
case 'React.AbstractComponent':
|
|
2068
|
+
case 'React$AbstractComponent': {
|
|
1620
2069
|
const typeParameters = node.typeParameters;
|
|
1621
2070
|
if (typeParameters == null || typeParameters.params.length === 0) {
|
|
1622
2071
|
throw translationError(
|
|
@@ -1632,50 +2081,142 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1632
2081
|
);
|
|
1633
2082
|
}
|
|
1634
2083
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
2084
|
+
const newParams = (() => {
|
|
2085
|
+
if (params.length === 1) {
|
|
2086
|
+
return assertHasExactlyNTypeParameters(1);
|
|
2087
|
+
}
|
|
2088
|
+
|
|
2089
|
+
const [props, ref] = assertHasExactlyNTypeParameters(2);
|
|
2090
|
+
|
|
2091
|
+
return [
|
|
2092
|
+
{
|
|
2093
|
+
type: 'TSIntersectionType',
|
|
2094
|
+
types: [
|
|
2095
|
+
props,
|
|
2096
|
+
{
|
|
2097
|
+
type: 'TSTypeReference',
|
|
2098
|
+
typeName: {
|
|
2099
|
+
type: 'TSQualifiedName',
|
|
2100
|
+
left: {
|
|
2101
|
+
type: 'Identifier',
|
|
2102
|
+
name: 'React',
|
|
2103
|
+
},
|
|
2104
|
+
right: {
|
|
2105
|
+
type: 'Identifier',
|
|
2106
|
+
name: 'RefAttributes',
|
|
2107
|
+
},
|
|
2108
|
+
},
|
|
2109
|
+
typeParameters: {
|
|
2110
|
+
type: 'TSTypeParameterInstantiation',
|
|
2111
|
+
params: [ref],
|
|
2112
|
+
},
|
|
2113
|
+
},
|
|
2114
|
+
],
|
|
2115
|
+
},
|
|
2116
|
+
];
|
|
2117
|
+
})();
|
|
2118
|
+
|
|
2119
|
+
return {
|
|
2120
|
+
type: 'TSTypeReference',
|
|
2121
|
+
typeName: {
|
|
2122
|
+
type: 'TSQualifiedName',
|
|
2123
|
+
left: getReactIdentifier(),
|
|
2124
|
+
right: {
|
|
2125
|
+
type: 'Identifier',
|
|
2126
|
+
name: 'ComponentType',
|
|
2127
|
+
},
|
|
2128
|
+
},
|
|
2129
|
+
typeParameters: {
|
|
2130
|
+
type: 'TSTypeParameterInstantiation',
|
|
2131
|
+
params: newParams,
|
|
2132
|
+
},
|
|
2133
|
+
};
|
|
2134
|
+
}
|
|
2135
|
+
// React.ElementConfig<A> -> JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
|
|
2136
|
+
// React$ElementConfig<A> -> JSX.LibraryManagedAttributes<A, React.ComponentProps<A>>
|
|
2137
|
+
case 'React.ElementConfig':
|
|
2138
|
+
case 'React$ElementConfig': {
|
|
2139
|
+
const [param] = assertHasExactlyNTypeParameters(1);
|
|
2140
|
+
return {
|
|
2141
|
+
type: 'TSTypeReference',
|
|
2142
|
+
typeName: {
|
|
2143
|
+
type: 'TSQualifiedName',
|
|
2144
|
+
left: {
|
|
2145
|
+
type: 'Identifier',
|
|
2146
|
+
name: 'JSX',
|
|
2147
|
+
},
|
|
2148
|
+
right: {
|
|
2149
|
+
type: 'Identifier',
|
|
2150
|
+
name: 'LibraryManagedAttributes',
|
|
2151
|
+
},
|
|
2152
|
+
},
|
|
2153
|
+
typeParameters: {
|
|
2154
|
+
type: 'TSTypeParameterInstantiation',
|
|
2155
|
+
params: [
|
|
2156
|
+
param,
|
|
1641
2157
|
{
|
|
1642
2158
|
type: 'TSTypeReference',
|
|
1643
2159
|
typeName: {
|
|
1644
2160
|
type: 'TSQualifiedName',
|
|
1645
|
-
left:
|
|
1646
|
-
type: 'Identifier',
|
|
1647
|
-
name: 'React',
|
|
1648
|
-
},
|
|
2161
|
+
left: getReactIdentifier(),
|
|
1649
2162
|
right: {
|
|
1650
2163
|
type: 'Identifier',
|
|
1651
|
-
name:
|
|
2164
|
+
name: `ComponentProps`,
|
|
1652
2165
|
},
|
|
1653
2166
|
},
|
|
1654
2167
|
typeParameters: {
|
|
1655
2168
|
type: 'TSTypeParameterInstantiation',
|
|
1656
|
-
params: [
|
|
2169
|
+
params: [param],
|
|
1657
2170
|
},
|
|
1658
2171
|
},
|
|
1659
2172
|
],
|
|
1660
|
-
}
|
|
1661
|
-
}
|
|
1662
|
-
|
|
2173
|
+
},
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
// React.Ref<C> -> NonNullable<React.Ref<C> | string | number>
|
|
2177
|
+
// React$Ref<C> -> NonNullable<React.Ref<C> | string | number>
|
|
2178
|
+
case 'React.Ref':
|
|
2179
|
+
case 'React$Ref':
|
|
1663
2180
|
return {
|
|
1664
2181
|
type: 'TSTypeReference',
|
|
1665
2182
|
typeName: {
|
|
1666
|
-
type: '
|
|
1667
|
-
|
|
1668
|
-
right: {
|
|
1669
|
-
type: 'Identifier',
|
|
1670
|
-
name: `ForwardRefExoticComponent`,
|
|
1671
|
-
},
|
|
2183
|
+
type: 'Identifier',
|
|
2184
|
+
name: 'NonNullable',
|
|
1672
2185
|
},
|
|
1673
2186
|
typeParameters: {
|
|
1674
2187
|
type: 'TSTypeParameterInstantiation',
|
|
1675
|
-
params: [
|
|
2188
|
+
params: [
|
|
2189
|
+
{
|
|
2190
|
+
type: 'TSUnionType',
|
|
2191
|
+
types: [
|
|
2192
|
+
{
|
|
2193
|
+
type: 'TSTypeReference',
|
|
2194
|
+
typeName: {
|
|
2195
|
+
type: 'TSQualifiedName',
|
|
2196
|
+
left: getReactIdentifier(),
|
|
2197
|
+
right: {
|
|
2198
|
+
type: 'Identifier',
|
|
2199
|
+
name: 'Ref',
|
|
2200
|
+
},
|
|
2201
|
+
},
|
|
2202
|
+
typeParameters: {
|
|
2203
|
+
type: 'TSTypeParameterInstantiation',
|
|
2204
|
+
params: assertHasExactlyNTypeParameters(1),
|
|
2205
|
+
},
|
|
2206
|
+
},
|
|
2207
|
+
{
|
|
2208
|
+
type: 'TSStringKeyword',
|
|
2209
|
+
},
|
|
2210
|
+
{
|
|
2211
|
+
type: 'TSNumberKeyword',
|
|
2212
|
+
},
|
|
2213
|
+
],
|
|
2214
|
+
},
|
|
2215
|
+
],
|
|
1676
2216
|
},
|
|
1677
2217
|
};
|
|
1678
|
-
|
|
2218
|
+
default:
|
|
2219
|
+
return unsupportedAnnotation(node, fullTypeName);
|
|
1679
2220
|
}
|
|
1680
2221
|
}
|
|
1681
2222
|
|
|
@@ -1710,8 +2251,8 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1710
2251
|
): TSESTree.TSIndexedAccessType {
|
|
1711
2252
|
return {
|
|
1712
2253
|
type: 'TSIndexedAccessType',
|
|
1713
|
-
objectType:
|
|
1714
|
-
indexType:
|
|
2254
|
+
objectType: transformTypeAnnotationType(node.objectType),
|
|
2255
|
+
indexType: transformTypeAnnotationType(node.indexType),
|
|
1715
2256
|
};
|
|
1716
2257
|
},
|
|
1717
2258
|
InterfaceDeclaration(
|
|
@@ -1733,67 +2274,77 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1733
2274
|
},
|
|
1734
2275
|
ImportDeclaration(
|
|
1735
2276
|
node: FlowESTree.ImportDeclaration,
|
|
1736
|
-
): TSESTree.ImportDeclaration {
|
|
1737
|
-
|
|
1738
|
-
/*
|
|
1739
|
-
TODO - this is a complicated change to support because TS
|
|
1740
|
-
does not have typeof imports.
|
|
1741
|
-
Making it a `type` import would change the meaning!
|
|
1742
|
-
The only way to truly support this is to prefix all **usages** with `typeof T`.
|
|
1743
|
-
eg:
|
|
2277
|
+
): Array<DeclarationOrUnsupported<TSESTree.ImportDeclaration>> {
|
|
2278
|
+
const importKind = node.importKind;
|
|
1744
2279
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
2280
|
+
const specifiers = [];
|
|
2281
|
+
const unsupportedSpecifiers = [];
|
|
2282
|
+
node.specifiers.forEach(spec => {
|
|
2283
|
+
let id = (() => {
|
|
2284
|
+
if (node.importKind === 'typeof' || spec.importKind === 'typeof') {
|
|
2285
|
+
const id = {
|
|
2286
|
+
type: 'Identifier',
|
|
2287
|
+
name: getPlaceholderNameForTypeofImport(),
|
|
2288
|
+
};
|
|
1749
2289
|
|
|
1750
|
-
|
|
2290
|
+
unsupportedSpecifiers.push({
|
|
2291
|
+
type: 'TSTypeAliasDeclaration',
|
|
2292
|
+
id: transform.Identifier(spec.local, false),
|
|
2293
|
+
typeAnnotation: {
|
|
2294
|
+
type: 'TSTypeQuery',
|
|
2295
|
+
exprName: id,
|
|
2296
|
+
},
|
|
2297
|
+
});
|
|
1751
2298
|
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
type T = typeof Foo;
|
|
1755
|
-
```
|
|
2299
|
+
return id;
|
|
2300
|
+
}
|
|
1756
2301
|
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
*/
|
|
1760
|
-
throw unsupportedTranslationError(node, 'typeof imports');
|
|
1761
|
-
}
|
|
1762
|
-
const importKind = node.importKind;
|
|
2302
|
+
return transform.Identifier(spec.local, false);
|
|
2303
|
+
})();
|
|
1763
2304
|
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
case 'ImportDefaultSpecifier':
|
|
1772
|
-
return {
|
|
1773
|
-
type: 'ImportDefaultSpecifier',
|
|
1774
|
-
local: transform.Identifier(spec.local, false),
|
|
1775
|
-
};
|
|
2305
|
+
switch (spec.type) {
|
|
2306
|
+
case 'ImportDefaultSpecifier':
|
|
2307
|
+
specifiers.push({
|
|
2308
|
+
type: 'ImportDefaultSpecifier',
|
|
2309
|
+
local: id,
|
|
2310
|
+
});
|
|
2311
|
+
return;
|
|
1776
2312
|
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
2313
|
+
case 'ImportNamespaceSpecifier':
|
|
2314
|
+
specifiers.push({
|
|
2315
|
+
type: 'ImportNamespaceSpecifier',
|
|
2316
|
+
local: id,
|
|
2317
|
+
});
|
|
2318
|
+
return;
|
|
2319
|
+
|
|
2320
|
+
case 'ImportSpecifier':
|
|
2321
|
+
specifiers.push({
|
|
2322
|
+
type: 'ImportSpecifier',
|
|
2323
|
+
importKind:
|
|
2324
|
+
spec.importKind === 'typeof' || spec.importKind === 'type'
|
|
2325
|
+
? 'type'
|
|
2326
|
+
: null,
|
|
2327
|
+
imported: transform.Identifier(spec.imported, false),
|
|
2328
|
+
local: id,
|
|
2329
|
+
});
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
});
|
|
2333
|
+
|
|
2334
|
+
const out = specifiers.length
|
|
2335
|
+
? [
|
|
2336
|
+
{
|
|
2337
|
+
type: 'ImportDeclaration',
|
|
2338
|
+
assertions: node.assertions.map(transform.ImportAttribute),
|
|
2339
|
+
importKind:
|
|
2340
|
+
importKind === 'typeof' ? 'type' : importKind ?? 'value',
|
|
2341
|
+
source: transform.StringLiteral(node.source),
|
|
2342
|
+
specifiers,
|
|
2343
|
+
},
|
|
2344
|
+
]
|
|
2345
|
+
: [];
|
|
1782
2346
|
|
|
1783
|
-
|
|
1784
|
-
if (spec.importKind === 'typeof') {
|
|
1785
|
-
// see above
|
|
1786
|
-
throw unsupportedTranslationError(node, 'typeof imports');
|
|
1787
|
-
}
|
|
1788
|
-
return {
|
|
1789
|
-
type: 'ImportSpecifier',
|
|
1790
|
-
importKind: spec.importKind ?? 'value',
|
|
1791
|
-
imported: transform.Identifier(spec.imported, false),
|
|
1792
|
-
local: transform.Identifier(spec.local, false),
|
|
1793
|
-
};
|
|
1794
|
-
}
|
|
1795
|
-
}),
|
|
1796
|
-
};
|
|
2347
|
+
return [...out, ...unsupportedSpecifiers];
|
|
1797
2348
|
},
|
|
1798
2349
|
InterfaceExtends(
|
|
1799
2350
|
node: FlowESTree.InterfaceExtends,
|
|
@@ -1837,7 +2388,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1837
2388
|
): TSESTree.TSIntersectionType {
|
|
1838
2389
|
return {
|
|
1839
2390
|
type: 'TSIntersectionType',
|
|
1840
|
-
types: node.types.map(
|
|
2391
|
+
types: node.types.map(transformTypeAnnotationType),
|
|
1841
2392
|
};
|
|
1842
2393
|
},
|
|
1843
2394
|
Literal(node: FlowESTree.Literal): TSESTree.Literal {
|
|
@@ -1891,7 +2442,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1891
2442
|
{
|
|
1892
2443
|
type: 'TSUndefinedKeyword',
|
|
1893
2444
|
},
|
|
1894
|
-
|
|
2445
|
+
transformTypeAnnotationType(node.typeAnnotation),
|
|
1895
2446
|
],
|
|
1896
2447
|
};
|
|
1897
2448
|
},
|
|
@@ -1923,7 +2474,10 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1923
2474
|
},
|
|
1924
2475
|
ObjectTypeAnnotation(
|
|
1925
2476
|
node: FlowESTree.ObjectTypeAnnotation,
|
|
1926
|
-
):
|
|
2477
|
+
):
|
|
2478
|
+
| TSESTree.TSTypeLiteral
|
|
2479
|
+
| TSESTree.TSIntersectionType
|
|
2480
|
+
| TSESTree.TSAnyKeyword {
|
|
1927
2481
|
// we want to preserve the source order of the members
|
|
1928
2482
|
// unfortunately flow has unordered properties storing things
|
|
1929
2483
|
// so store all elements with their start index and sort the
|
|
@@ -1950,10 +2504,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1950
2504
|
They're really rarely used (if ever) - so let's just ignore them for now
|
|
1951
2505
|
*/
|
|
1952
2506
|
if (node.internalSlots.length > 0) {
|
|
1953
|
-
|
|
1954
|
-
node.internalSlots[0],
|
|
1955
|
-
'internal slots',
|
|
1956
|
-
);
|
|
2507
|
+
return unsupportedAnnotation(node.internalSlots[0], 'internal slots');
|
|
1957
2508
|
}
|
|
1958
2509
|
|
|
1959
2510
|
if (!node.properties.find(FlowESTree.isObjectTypeSpreadProperty)) {
|
|
@@ -1964,6 +2515,14 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
1964
2515
|
throw unexpectedTranslationError(property, 'Impossible state');
|
|
1965
2516
|
}
|
|
1966
2517
|
|
|
2518
|
+
if (property.type === 'ObjectTypeMappedTypeProperty') {
|
|
2519
|
+
// TODO - Support mapped types
|
|
2520
|
+
return unsupportedAnnotation(
|
|
2521
|
+
property,
|
|
2522
|
+
'object type with mapped type property',
|
|
2523
|
+
);
|
|
2524
|
+
}
|
|
2525
|
+
|
|
1967
2526
|
members.push({
|
|
1968
2527
|
start: property.range[0],
|
|
1969
2528
|
node: transform.ObjectTypeProperty(property),
|
|
@@ -2050,7 +2609,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2050
2609
|
*/
|
|
2051
2610
|
|
|
2052
2611
|
if (members.length > 0) {
|
|
2053
|
-
|
|
2612
|
+
return unsupportedAnnotation(
|
|
2054
2613
|
node,
|
|
2055
2614
|
'object types with spreads, indexers and/or call properties at the same time',
|
|
2056
2615
|
);
|
|
@@ -2060,21 +2619,27 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2060
2619
|
for (const property of node.properties) {
|
|
2061
2620
|
if (property.type === 'ObjectTypeSpreadProperty') {
|
|
2062
2621
|
if (members.length > 0) {
|
|
2063
|
-
|
|
2622
|
+
return unsupportedAnnotation(
|
|
2064
2623
|
property,
|
|
2065
2624
|
'object types with spreads in the middle or at the end',
|
|
2066
2625
|
);
|
|
2067
2626
|
}
|
|
2068
2627
|
|
|
2069
|
-
const spreadType =
|
|
2628
|
+
const spreadType = transformTypeAnnotationType(property.argument);
|
|
2070
2629
|
if (spreadType.type !== 'TSTypeReference') {
|
|
2071
|
-
|
|
2630
|
+
return unsupportedAnnotation(
|
|
2072
2631
|
property,
|
|
2073
2632
|
'object types with complex spreads',
|
|
2074
2633
|
);
|
|
2075
2634
|
}
|
|
2076
2635
|
|
|
2077
2636
|
typesToIntersect.push(spreadType);
|
|
2637
|
+
} else if (property.type === 'ObjectTypeMappedTypeProperty') {
|
|
2638
|
+
// TODO - Support mapped types
|
|
2639
|
+
return unsupportedAnnotation(
|
|
2640
|
+
property,
|
|
2641
|
+
'object type with mapped type property',
|
|
2642
|
+
);
|
|
2078
2643
|
} else {
|
|
2079
2644
|
members.push({
|
|
2080
2645
|
start: property.range[0],
|
|
@@ -2148,7 +2713,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2148
2713
|
name: node.id == null ? '$$Key$$' : node.id.name,
|
|
2149
2714
|
typeAnnotation: {
|
|
2150
2715
|
type: 'TSTypeAnnotation',
|
|
2151
|
-
typeAnnotation:
|
|
2716
|
+
typeAnnotation: transformTypeAnnotationType(node.key),
|
|
2152
2717
|
},
|
|
2153
2718
|
},
|
|
2154
2719
|
],
|
|
@@ -2156,7 +2721,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2156
2721
|
static: node.static,
|
|
2157
2722
|
typeAnnotation: {
|
|
2158
2723
|
type: 'TSTypeAnnotation',
|
|
2159
|
-
typeAnnotation:
|
|
2724
|
+
typeAnnotation: transformTypeAnnotationType(node.value),
|
|
2160
2725
|
},
|
|
2161
2726
|
};
|
|
2162
2727
|
},
|
|
@@ -2213,7 +2778,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2213
2778
|
static: node.static,
|
|
2214
2779
|
typeAnnotation: {
|
|
2215
2780
|
type: 'TSTypeAnnotation',
|
|
2216
|
-
typeAnnotation:
|
|
2781
|
+
typeAnnotation: transformTypeAnnotationType(node.value),
|
|
2217
2782
|
},
|
|
2218
2783
|
};
|
|
2219
2784
|
},
|
|
@@ -2242,10 +2807,10 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2242
2807
|
},
|
|
2243
2808
|
typeParameters: {
|
|
2244
2809
|
type: 'TSTypeParameterInstantiation',
|
|
2245
|
-
params: [
|
|
2810
|
+
params: [transformTypeAnnotationType(node.objectType)],
|
|
2246
2811
|
},
|
|
2247
2812
|
},
|
|
2248
|
-
indexType:
|
|
2813
|
+
indexType: transformTypeAnnotationType(node.indexType),
|
|
2249
2814
|
};
|
|
2250
2815
|
},
|
|
2251
2816
|
QualifiedTypeIdentifier(
|
|
@@ -2262,6 +2827,20 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2262
2827
|
right: transform.Identifier(node.id, false),
|
|
2263
2828
|
};
|
|
2264
2829
|
},
|
|
2830
|
+
QualifiedTypeofIdentifier(
|
|
2831
|
+
node: FlowESTree.QualifiedTypeofIdentifier,
|
|
2832
|
+
): TSESTree.TSQualifiedName {
|
|
2833
|
+
const qual = node.qualification;
|
|
2834
|
+
|
|
2835
|
+
return {
|
|
2836
|
+
type: 'TSQualifiedName',
|
|
2837
|
+
left:
|
|
2838
|
+
qual.type === 'Identifier'
|
|
2839
|
+
? transform.Identifier(qual, false)
|
|
2840
|
+
: transform.QualifiedTypeofIdentifier(qual),
|
|
2841
|
+
right: transform.Identifier(node.id, false),
|
|
2842
|
+
};
|
|
2843
|
+
},
|
|
2265
2844
|
RegExpLiteral(node: FlowESTree.RegExpLiteral): TSESTree.RegExpLiteral {
|
|
2266
2845
|
return {
|
|
2267
2846
|
type: 'Literal',
|
|
@@ -2318,7 +2897,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2318
2897
|
): TSESTree.TSTupleType {
|
|
2319
2898
|
return {
|
|
2320
2899
|
type: 'TSTupleType',
|
|
2321
|
-
elementTypes: node.types.map(
|
|
2900
|
+
elementTypes: node.types.map(transformTypeAnnotationType),
|
|
2322
2901
|
};
|
|
2323
2902
|
},
|
|
2324
2903
|
TypeAlias(node: FlowESTree.TypeAlias): TSESTree.TSTypeAliasDeclaration {
|
|
@@ -2327,89 +2906,26 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2327
2906
|
TypeAnnotation(node: FlowESTree.TypeAnnotation): TSESTree.TSTypeAnnotation {
|
|
2328
2907
|
return {
|
|
2329
2908
|
type: 'TSTypeAnnotation',
|
|
2330
|
-
typeAnnotation:
|
|
2909
|
+
typeAnnotation: transformTypeAnnotationType(node.typeAnnotation),
|
|
2331
2910
|
};
|
|
2332
2911
|
},
|
|
2333
|
-
TypeAnnotationType(node: FlowESTree.TypeAnnotationType): TSESTree.TypeNode {
|
|
2334
|
-
switch (node.type) {
|
|
2335
|
-
case 'AnyTypeAnnotation':
|
|
2336
|
-
return transform.AnyTypeAnnotation(node);
|
|
2337
|
-
case 'ArrayTypeAnnotation':
|
|
2338
|
-
return transform.ArrayTypeAnnotation(node);
|
|
2339
|
-
case 'BigIntLiteralTypeAnnotation':
|
|
2340
|
-
return transform.BigIntLiteralTypeAnnotation(node);
|
|
2341
|
-
case 'BigIntTypeAnnotation':
|
|
2342
|
-
return transform.BigIntTypeAnnotation(node);
|
|
2343
|
-
case 'BooleanLiteralTypeAnnotation':
|
|
2344
|
-
return transform.BooleanLiteralTypeAnnotation(node);
|
|
2345
|
-
case 'BooleanTypeAnnotation':
|
|
2346
|
-
return transform.BooleanTypeAnnotation(node);
|
|
2347
|
-
case 'EmptyTypeAnnotation':
|
|
2348
|
-
return transform.EmptyTypeAnnotation(node);
|
|
2349
|
-
case 'ExistsTypeAnnotation':
|
|
2350
|
-
return transform.ExistsTypeAnnotation(node);
|
|
2351
|
-
case 'FunctionTypeAnnotation':
|
|
2352
|
-
return transform.FunctionTypeAnnotation(node);
|
|
2353
|
-
case 'GenericTypeAnnotation':
|
|
2354
|
-
return transform.GenericTypeAnnotation(node);
|
|
2355
|
-
case 'IndexedAccessType':
|
|
2356
|
-
return transform.IndexedAccessType(node);
|
|
2357
|
-
case 'InterfaceTypeAnnotation':
|
|
2358
|
-
return transform.InterfaceTypeAnnotation(node);
|
|
2359
|
-
case 'IntersectionTypeAnnotation':
|
|
2360
|
-
return transform.IntersectionTypeAnnotation(node);
|
|
2361
|
-
case 'MixedTypeAnnotation':
|
|
2362
|
-
return transform.MixedTypeAnnotation(node);
|
|
2363
|
-
case 'NullLiteralTypeAnnotation':
|
|
2364
|
-
return transform.NullLiteralTypeAnnotation(node);
|
|
2365
|
-
case 'NullableTypeAnnotation':
|
|
2366
|
-
return transform.NullableTypeAnnotation(node);
|
|
2367
|
-
case 'NumberLiteralTypeAnnotation':
|
|
2368
|
-
return transform.NumberLiteralTypeAnnotation(node);
|
|
2369
|
-
case 'NumberTypeAnnotation':
|
|
2370
|
-
return transform.NumberTypeAnnotation(node);
|
|
2371
|
-
case 'ObjectTypeAnnotation':
|
|
2372
|
-
return transform.ObjectTypeAnnotation(node);
|
|
2373
|
-
case 'OptionalIndexedAccessType':
|
|
2374
|
-
return transform.OptionalIndexedAccessType(node);
|
|
2375
|
-
case 'QualifiedTypeIdentifier':
|
|
2376
|
-
return transform.QualifiedTypeIdentifier(node);
|
|
2377
|
-
case 'StringLiteralTypeAnnotation':
|
|
2378
|
-
return transform.StringLiteralTypeAnnotation(node);
|
|
2379
|
-
case 'StringTypeAnnotation':
|
|
2380
|
-
return transform.StringTypeAnnotation(node);
|
|
2381
|
-
case 'SymbolTypeAnnotation':
|
|
2382
|
-
return transform.SymbolTypeAnnotation(node);
|
|
2383
|
-
case 'ThisTypeAnnotation':
|
|
2384
|
-
return transform.ThisTypeAnnotation(node);
|
|
2385
|
-
case 'TupleTypeAnnotation':
|
|
2386
|
-
return transform.TupleTypeAnnotation(node);
|
|
2387
|
-
case 'TypeofTypeAnnotation':
|
|
2388
|
-
return transform.TypeofTypeAnnotation(node);
|
|
2389
|
-
case 'UnionTypeAnnotation':
|
|
2390
|
-
return transform.UnionTypeAnnotation(node);
|
|
2391
|
-
case 'VoidTypeAnnotation':
|
|
2392
|
-
return transform.VoidTypeAnnotation(node);
|
|
2393
|
-
default:
|
|
2394
|
-
throw unexpectedTranslationError(node, `Unhandled type ${node.type}`);
|
|
2395
|
-
}
|
|
2396
|
-
},
|
|
2397
2912
|
TypeofTypeAnnotation(
|
|
2398
2913
|
node: FlowESTree.TypeofTypeAnnotation,
|
|
2399
2914
|
): TSESTree.TSTypeQuery {
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2915
|
+
switch (node.argument.type) {
|
|
2916
|
+
case 'Identifier':
|
|
2917
|
+
return {
|
|
2918
|
+
type: 'TSTypeQuery',
|
|
2919
|
+
exprName: transform.Identifier(node.argument),
|
|
2920
|
+
typeParameters: undefined,
|
|
2921
|
+
};
|
|
2922
|
+
case 'QualifiedTypeofIdentifier':
|
|
2923
|
+
return {
|
|
2924
|
+
type: 'TSTypeQuery',
|
|
2925
|
+
exprName: transform.QualifiedTypeofIdentifier(node.argument),
|
|
2926
|
+
typeParameters: undefined,
|
|
2927
|
+
};
|
|
2406
2928
|
}
|
|
2407
|
-
|
|
2408
|
-
return {
|
|
2409
|
-
type: 'TSTypeQuery',
|
|
2410
|
-
exprName: argument.typeName,
|
|
2411
|
-
typeParameters: argument.typeParameters,
|
|
2412
|
-
};
|
|
2413
2929
|
},
|
|
2414
2930
|
TypeParameter(node: FlowESTree.TypeParameter): TSESTree.TSTypeParameter {
|
|
2415
2931
|
/*
|
|
@@ -2442,11 +2958,11 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2442
2958
|
constraint:
|
|
2443
2959
|
node.bound == null
|
|
2444
2960
|
? undefined
|
|
2445
|
-
:
|
|
2961
|
+
: transformTypeAnnotationType(node.bound.typeAnnotation),
|
|
2446
2962
|
default:
|
|
2447
2963
|
node.default == null
|
|
2448
2964
|
? undefined
|
|
2449
|
-
:
|
|
2965
|
+
: transformTypeAnnotationType(node.default),
|
|
2450
2966
|
in: false,
|
|
2451
2967
|
out: false,
|
|
2452
2968
|
// in: variance.has('in'),
|
|
@@ -2466,7 +2982,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2466
2982
|
): TSESTree.TSTypeParameterInstantiation {
|
|
2467
2983
|
return {
|
|
2468
2984
|
type: 'TSTypeParameterInstantiation',
|
|
2469
|
-
params: node.params.map(
|
|
2985
|
+
params: node.params.map(transformTypeAnnotationType),
|
|
2470
2986
|
};
|
|
2471
2987
|
},
|
|
2472
2988
|
UnionTypeAnnotation(
|
|
@@ -2474,7 +2990,7 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2474
2990
|
): TSESTree.TSUnionType {
|
|
2475
2991
|
return {
|
|
2476
2992
|
type: 'TSUnionType',
|
|
2477
|
-
types: node.types.map(
|
|
2993
|
+
types: node.types.map(transformTypeAnnotationType),
|
|
2478
2994
|
};
|
|
2479
2995
|
},
|
|
2480
2996
|
VoidTypeAnnotation(
|
|
@@ -2503,5 +3019,5 @@ const getTransforms = (code: string, scopeManager: ScopeManager) => {
|
|
|
2503
3019
|
};
|
|
2504
3020
|
}
|
|
2505
3021
|
|
|
2506
|
-
return transform;
|
|
3022
|
+
return [transform, code];
|
|
2507
3023
|
};
|