Skip to content

Commit 87c356d

Browse files
Add Map / indexed object support for TypeScript parser (facebook#35098)
Summary: Pull Request resolved: facebook#35098 Changelog: [General][Fixed] [react-native-codegen] react-native-codegen : Add Map / indexed object support for TypeScript parser In flow we can expose Maps via the following syntax in TM specs ` +getMap: (arg: {[key: string]: ?number}) => {[key: string]: ?number}; ` In TypeScript writing the same spec: ` readonly getMap: (arg: { [key: string]: number | null; }) => { [key: string]: number | null; }; ` leads to an exception the TypeScript code-gen parser ```UnsupportedObjectPropertyTypeAnnotationParserError: Module NativeTurboModuleCxx: 'ObjectTypeAnnotation' cannot contain 'TSIndexSignature'. at react-native-github/packages/react-native-codegen/src/parsers/typescript/modules/index.js:309:23``` ``` This change fixes the TypeScript parser Reviewed By: cipolleschi Differential Revision: D40753368 fbshipit-source-id: 0eef8ecb63d1ed049fde1e75cc6f2ec627f1f232
1 parent 87d6580 commit 87c356d

File tree

7 files changed

+215
-11
lines changed

7 files changed

+215
-11
lines changed

packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,8 @@ export interface Spec extends TurboModule {
644644
+getCallback: () => () => void;
645645
+getMixed: (arg: mixed) => mixed;
646646
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
647+
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
648+
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
647649
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
648650
}
649651

packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,80 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
122122
]
123123
}
124124
},
125+
{
126+
'name': 'getMap',
127+
'optional': false,
128+
'typeAnnotation': {
129+
'type': 'FunctionTypeAnnotation',
130+
'returnTypeAnnotation': {
131+
'type': 'ObjectTypeAnnotation',
132+
'properties': [
133+
{
134+
'name': 'b',
135+
'optional': false,
136+
'typeAnnotation': {
137+
'type': 'GenericObjectTypeAnnotation'
138+
}
139+
}
140+
]
141+
},
142+
'params': [
143+
{
144+
'name': 'arg',
145+
'optional': false,
146+
'typeAnnotation': {
147+
'type': 'ObjectTypeAnnotation',
148+
'properties': [
149+
{
150+
'name': 'a',
151+
'optional': false,
152+
'typeAnnotation': {
153+
'type': 'GenericObjectTypeAnnotation'
154+
}
155+
}
156+
]
157+
}
158+
}
159+
]
160+
}
161+
},
162+
{
163+
'name': 'getAnotherMap',
164+
'optional': false,
165+
'typeAnnotation': {
166+
'type': 'FunctionTypeAnnotation',
167+
'returnTypeAnnotation': {
168+
'type': 'ObjectTypeAnnotation',
169+
'properties': [
170+
{
171+
'name': 'key',
172+
'optional': false,
173+
'typeAnnotation': {
174+
'type': 'GenericObjectTypeAnnotation'
175+
}
176+
}
177+
]
178+
},
179+
'params': [
180+
{
181+
'name': 'arg',
182+
'optional': false,
183+
'typeAnnotation': {
184+
'type': 'ObjectTypeAnnotation',
185+
'properties': [
186+
{
187+
'name': 'key',
188+
'optional': false,
189+
'typeAnnotation': {
190+
'type': 'GenericObjectTypeAnnotation'
191+
}
192+
}
193+
]
194+
}
195+
}
196+
]
197+
}
198+
},
125199
{
126200
'name': 'getUnion',
127201
'optional': false,

packages/react-native-codegen/src/parsers/flow/modules/index.js

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ const {
6262
UnsupportedGenericParserError,
6363
UnsupportedTypeAnnotationParserError,
6464
UnsupportedEnumDeclarationParserError,
65-
UnsupportedUnionTypeAnnotationParserError,
6665
UnsupportedObjectPropertyTypeAnnotationParserError,
6766
IncorrectModuleRegistryCallArgumentTypeParserError,
6867
} = require('../../errors.js');
@@ -84,6 +83,7 @@ const {
8483
} = require('../../error-utils');
8584

8685
const {FlowParser} = require('../parser.js');
86+
const {getKeyName} = require('../../parsers-commons');
8787

8888
const language = 'Flow';
8989
const parser = new FlowParser();
@@ -287,11 +287,17 @@ function translateTypeAnnotation(
287287
const objectTypeAnnotation = {
288288
type: 'ObjectTypeAnnotation',
289289
// $FlowFixMe[missing-type-arg]
290-
properties: (typeAnnotation.properties: Array<$FlowFixMe>)
290+
properties: ([
291+
...typeAnnotation.properties,
292+
...typeAnnotation.indexers,
293+
]: Array<$FlowFixMe>)
291294
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
292295
property => {
293296
return tryParse(() => {
294-
if (property.type !== 'ObjectTypeProperty') {
297+
if (
298+
property.type !== 'ObjectTypeProperty' &&
299+
property.type !== 'ObjectTypeIndexer'
300+
) {
295301
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
296302
hasteModuleName,
297303
property,
@@ -300,8 +306,15 @@ function translateTypeAnnotation(
300306
);
301307
}
302308

303-
const {optional, key} = property;
304-
309+
const {optional = false} = property;
310+
const name = getKeyName(property, hasteModuleName, language);
311+
if (property.type === 'ObjectTypeIndexer') {
312+
return {
313+
name,
314+
optional,
315+
typeAnnotation: emitObject(nullable),
316+
};
317+
}
305318
const [propertyTypeAnnotation, isPropertyNullable] =
306319
unwrapNullable(
307320
translateTypeAnnotation(
@@ -328,7 +341,7 @@ function translateTypeAnnotation(
328341
);
329342
} else {
330343
return {
331-
name: key.name,
344+
name,
332345
optional,
333346
typeAnnotation: wrapNullable(
334347
isPropertyNullable,

packages/react-native-codegen/src/parsers/parsers-commons.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ const {
2424
UnsupportedUnionTypeAnnotationParserError,
2525
} = require('./errors');
2626
import type {ParserType} from './errors';
27-
27+
const {
28+
UnsupportedObjectPropertyTypeAnnotationParserError,
29+
} = require('./errors');
2830
const invariant = require('invariant');
2931

3032
function wrapModuleSchema(
@@ -155,11 +157,37 @@ function emitUnionTypeAnnotation(
155157
});
156158
}
157159

160+
function getKeyName(
161+
propertyOrIndex: $FlowFixMe,
162+
hasteModuleName: string,
163+
language: ParserType,
164+
): string {
165+
switch (propertyOrIndex.type) {
166+
case 'ObjectTypeProperty':
167+
case 'TSPropertySignature':
168+
return propertyOrIndex.key.name;
169+
case 'ObjectTypeIndexer':
170+
// flow index name is optional
171+
return propertyOrIndex.id?.name ?? 'key';
172+
case 'TSIndexSignature':
173+
// TypeScript index name is mandatory
174+
return propertyOrIndex.parameters[0].name;
175+
default:
176+
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
177+
hasteModuleName,
178+
propertyOrIndex,
179+
propertyOrIndex.type,
180+
language,
181+
);
182+
}
183+
}
184+
158185
module.exports = {
159186
wrapModuleSchema,
160187
unwrapNullable,
161188
wrapNullable,
162189
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
163190
emitMixedTypeAnnotation,
164191
emitUnionTypeAnnotation,
192+
getKeyName,
165193
};

packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,8 @@ export interface Spec extends TurboModule {
661661
readonly getCallback: () => () => void;
662662
readonly getMixed: (arg: unknown) => unknown;
663663
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
664+
readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;};
665+
readonly getAnotherMap: (arg: {[key: string]: string}) => {[key: string]: string};
664666
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
665667
}
666668

packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,80 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
120120
]
121121
}
122122
},
123+
{
124+
'name': 'getMap',
125+
'optional': false,
126+
'typeAnnotation': {
127+
'type': 'FunctionTypeAnnotation',
128+
'returnTypeAnnotation': {
129+
'type': 'ObjectTypeAnnotation',
130+
'properties': [
131+
{
132+
'name': 'b',
133+
'optional': false,
134+
'typeAnnotation': {
135+
'type': 'GenericObjectTypeAnnotation'
136+
}
137+
}
138+
]
139+
},
140+
'params': [
141+
{
142+
'name': 'arg',
143+
'optional': false,
144+
'typeAnnotation': {
145+
'type': 'ObjectTypeAnnotation',
146+
'properties': [
147+
{
148+
'name': 'a',
149+
'optional': false,
150+
'typeAnnotation': {
151+
'type': 'GenericObjectTypeAnnotation'
152+
}
153+
}
154+
]
155+
}
156+
}
157+
]
158+
}
159+
},
160+
{
161+
'name': 'getAnotherMap',
162+
'optional': false,
163+
'typeAnnotation': {
164+
'type': 'FunctionTypeAnnotation',
165+
'returnTypeAnnotation': {
166+
'type': 'ObjectTypeAnnotation',
167+
'properties': [
168+
{
169+
'name': 'key',
170+
'optional': false,
171+
'typeAnnotation': {
172+
'type': 'GenericObjectTypeAnnotation'
173+
}
174+
}
175+
]
176+
},
177+
'params': [
178+
{
179+
'name': 'arg',
180+
'optional': false,
181+
'typeAnnotation': {
182+
'type': 'ObjectTypeAnnotation',
183+
'properties': [
184+
{
185+
'name': 'key',
186+
'optional': false,
187+
'typeAnnotation': {
188+
'type': 'GenericObjectTypeAnnotation'
189+
}
190+
}
191+
]
192+
}
193+
}
194+
]
195+
}
196+
},
123197
{
124198
'name': 'getUnion',
125199
'optional': false,

packages/react-native-codegen/src/parsers/typescript/modules/index.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ const {
8484
} = require('../../error-utils');
8585

8686
const {TypeScriptParser} = require('../parser');
87+
const {getKeyName} = require('../../parsers-commons');
8788

8889
const language = 'TypeScript';
8990
const parser = new TypeScriptParser();
@@ -306,7 +307,10 @@ function translateTypeAnnotation(
306307
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
307308
property => {
308309
return tryParse(() => {
309-
if (property.type !== 'TSPropertySignature') {
310+
if (
311+
property.type !== 'TSPropertySignature' &&
312+
property.type !== 'TSIndexSignature'
313+
) {
310314
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
311315
hasteModuleName,
312316
property,
@@ -315,8 +319,15 @@ function translateTypeAnnotation(
315319
);
316320
}
317321

318-
const {optional = false, key} = property;
319-
322+
const {optional = false} = property;
323+
const name = getKeyName(property, hasteModuleName, language);
324+
if (property.type === 'TSIndexSignature') {
325+
return {
326+
name,
327+
optional,
328+
typeAnnotation: emitObject(nullable),
329+
};
330+
}
320331
const [propertyTypeAnnotation, isPropertyNullable] =
321332
unwrapNullable(
322333
translateTypeAnnotation(
@@ -343,7 +354,7 @@ function translateTypeAnnotation(
343354
);
344355
} else {
345356
return {
346-
name: key.name,
357+
name,
347358
optional,
348359
typeAnnotation: wrapNullable(
349360
isPropertyNullable,

0 commit comments

Comments
 (0)