Skip to content

Commit

Permalink
Add Map / indexed object support for TypeScript parser (#35098)
Browse files Browse the repository at this point in the history
Summary:
Pull Request resolved: #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
  • Loading branch information
christophpurrer authored and facebook-github-bot committed Oct 30, 2022
1 parent 87d6580 commit 87c356d
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ export interface Spec extends TurboModule {
+getCallback: () => () => void;
+getMixed: (arg: mixed) => mixed;
+getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
+getMap: (arg: {[a: string]: ?number}) => {[b: string]: ?number};
+getAnotherMap: (arg: {[string]: string}) => {[string]: string};
+getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,80 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
]
}
},
{
'name': 'getMap',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'b',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'a',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
}
}
]
}
},
{
'name': 'getAnotherMap',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
}
}
]
}
},
{
'name': 'getUnion',
'optional': false,
Expand Down
25 changes: 19 additions & 6 deletions packages/react-native-codegen/src/parsers/flow/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ const {
UnsupportedGenericParserError,
UnsupportedTypeAnnotationParserError,
UnsupportedEnumDeclarationParserError,
UnsupportedUnionTypeAnnotationParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
} = require('../../errors.js');
Expand All @@ -84,6 +83,7 @@ const {
} = require('../../error-utils');

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

const language = 'Flow';
const parser = new FlowParser();
Expand Down Expand Up @@ -287,11 +287,17 @@ function translateTypeAnnotation(
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: (typeAnnotation.properties: Array<$FlowFixMe>)
properties: ([
...typeAnnotation.properties,
...typeAnnotation.indexers,
]: Array<$FlowFixMe>)
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
if (property.type !== 'ObjectTypeProperty') {
if (
property.type !== 'ObjectTypeProperty' &&
property.type !== 'ObjectTypeIndexer'
) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
Expand All @@ -300,8 +306,15 @@ function translateTypeAnnotation(
);
}

const {optional, key} = property;
const {optional = false} = property;
const name = getKeyName(property, hasteModuleName, language);
if (property.type === 'ObjectTypeIndexer') {
return {
name,
optional,
typeAnnotation: emitObject(nullable),
};
}
const [propertyTypeAnnotation, isPropertyNullable] =
unwrapNullable(
translateTypeAnnotation(
Expand All @@ -328,7 +341,7 @@ function translateTypeAnnotation(
);
} else {
return {
name: key.name,
name,
optional,
typeAnnotation: wrapNullable(
isPropertyNullable,
Expand Down
30 changes: 29 additions & 1 deletion packages/react-native-codegen/src/parsers/parsers-commons.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ const {
UnsupportedUnionTypeAnnotationParserError,
} = require('./errors');
import type {ParserType} from './errors';

const {
UnsupportedObjectPropertyTypeAnnotationParserError,
} = require('./errors');
const invariant = require('invariant');

function wrapModuleSchema(
Expand Down Expand Up @@ -155,11 +157,37 @@ function emitUnionTypeAnnotation(
});
}

function getKeyName(
propertyOrIndex: $FlowFixMe,
hasteModuleName: string,
language: ParserType,
): string {
switch (propertyOrIndex.type) {
case 'ObjectTypeProperty':
case 'TSPropertySignature':
return propertyOrIndex.key.name;
case 'ObjectTypeIndexer':
// flow index name is optional
return propertyOrIndex.id?.name ?? 'key';
case 'TSIndexSignature':
// TypeScript index name is mandatory
return propertyOrIndex.parameters[0].name;
default:
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
propertyOrIndex,
propertyOrIndex.type,
language,
);
}
}

module.exports = {
wrapModuleSchema,
unwrapNullable,
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
emitMixedTypeAnnotation,
emitUnionTypeAnnotation,
getKeyName,
};
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,8 @@ export interface Spec extends TurboModule {
readonly getCallback: () => () => void;
readonly getMixed: (arg: unknown) => unknown;
readonly getEnums: (quality: Quality, resolution?: Resolution, floppy: Floppy, stringOptions: StringOptions) => string;
readonly getMap: (arg: {[a: string]: number | null;}) => {[b: string]: number | null;};
readonly getAnotherMap: (arg: {[key: string]: string}) => {[key: string]: string};
readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,80 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL
]
}
},
{
'name': 'getMap',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'b',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'a',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
}
}
]
}
},
{
'name': 'getAnotherMap',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
}
}
]
}
},
{
'name': 'getUnion',
'optional': false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const {
} = require('../../error-utils');

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

const language = 'TypeScript';
const parser = new TypeScriptParser();
Expand Down Expand Up @@ -306,7 +307,10 @@ function translateTypeAnnotation(
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
if (property.type !== 'TSPropertySignature') {
if (
property.type !== 'TSPropertySignature' &&
property.type !== 'TSIndexSignature'
) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
Expand All @@ -315,8 +319,15 @@ function translateTypeAnnotation(
);
}

const {optional = false, key} = property;

const {optional = false} = property;
const name = getKeyName(property, hasteModuleName, language);
if (property.type === 'TSIndexSignature') {
return {
name,
optional,
typeAnnotation: emitObject(nullable),
};
}
const [propertyTypeAnnotation, isPropertyNullable] =
unwrapNullable(
translateTypeAnnotation(
Expand All @@ -343,7 +354,7 @@ function translateTypeAnnotation(
);
} else {
return {
name: key.name,
name,
optional,
typeAnnotation: wrapNullable(
isPropertyNullable,
Expand Down

0 comments on commit 87c356d

Please sign in to comment.