From 355feafff6129129533c939e707fadb5fc747c08 Mon Sep 17 00:00:00 2001 From: Christoph Purrer Date: Wed, 24 Aug 2022 14:47:39 -0700 Subject: [PATCH] react-native-code-gen Add Union Type support for C++ TurboModules Summary: There are cases where we want to pass a union type into a TurboModule method which is handy to restrict certain arguments to a restricted set of values, e.g. restricting quality to ```'low'```, ```high``` or resolution to ```720```, ```1080```. - We are not generating an ```union``` type in C++ but rather just cast type safe to the corresponding member type. - We don't support mixed primitive union types at this time, e.g. ```export type ChooseSomething = 'One' | 'Two' | 3 | false;``` does not work. - We can support mixed object union types such as ... ```export type ChooseObject = {} | {low: string};``` - which need special logic in the C++ TM to correctly parse the resulting jsi::Object This is for C++ only, Java and ObjC are not supported atm. Changelog: [General][Added] - react-native-code-gen Add Union Type support for C++ TurboModules Reviewed By: javache Differential Revision: D38919688 fbshipit-source-id: 0fd37545b32b4f2059a8babda62dab4a85de37a9 --- .../react-native-codegen/src/CodegenSchema.js | 11 +++ .../generators/modules/GenerateModuleCpp.js | 13 ++++ .../src/generators/modules/GenerateModuleH.js | 11 +++ .../modules/GenerateModuleJavaSpec.js | 6 +- .../modules/GenerateModuleJniCpp.js | 6 +- .../GenerateModuleObjCpp/StructCollector.js | 2 + .../GenerateModuleObjCpp/serializeMethod.js | 4 +- .../modules/__test_fixtures__/fixtures.js | 45 ++++++++++++ .../GenerateModuleCpp-test.js.snap | 4 ++ .../GenerateModuleH-test.js.snap | 9 +++ .../modules/__test_fixtures__/fixtures.js | 6 ++ .../module-parser-snapshot-test.js.snap | 45 ++++++++++++ .../src/parsers/flow/modules/errors.js | 25 +++++++ .../src/parsers/flow/modules/index.js | 28 ++++++++ .../modules/__test_fixtures__/fixtures.js | 6 ++ ...script-module-parser-snapshot-test.js.snap | 69 +++++++++++++++---- .../src/parsers/typescript/modules/errors.js | 25 +++++++ .../src/parsers/typescript/modules/index.js | 28 ++++++++ 18 files changed, 323 insertions(+), 20 deletions(-) diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 8776caaa9232a9..8ffc00612548b4 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -295,6 +295,16 @@ export type NativeModulePromiseTypeAnnotation = $ReadOnly<{ type: 'PromiseTypeAnnotation', }>; +export type UnionTypeAnnotationMemberType = + | 'NumberTypeAnnotation' + | 'ObjectTypeAnnotation' + | 'StringTypeAnnotation'; + +export type NativeModuleUnionTypeAnnotation = $ReadOnly<{ + type: 'UnionTypeAnnotation', + memberType: UnionTypeAnnotationMemberType, +}>; + export type NativeModuleMixedTypeAnnotation = $ReadOnly<{ type: 'MixedTypeAnnotation', }>; @@ -311,6 +321,7 @@ export type NativeModuleBaseTypeAnnotation = | NativeModuleTypeAliasTypeAnnotation | NativeModuleArrayTypeAnnotation> | NativeModuleObjectTypeAnnotation + | NativeModuleUnionTypeAnnotation | NativeModuleMixedTypeAnnotation; export type NativeModuleParamTypeAnnotation = diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js index 046e86b1c65e0a..d49ed9253154e2 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleCpp.js @@ -167,6 +167,19 @@ function serializeArg( return wrap(val => `${val}.asObject(rt).asFunction(rt)`); case 'GenericObjectTypeAnnotation': return wrap(val => `${val}.asObject(rt)`); + case 'UnionTypeAnnotation': + switch (typeAnnotation.memberType) { + case 'NumberTypeAnnotation': + return wrap(val => `${val}.asNumber()`); + case 'ObjectTypeAnnotation': + return wrap(val => `${val}.asObject(rt)`); + case 'StringTypeAnnotation': + return wrap(val => `${val}.asString(rt)`); + default: + throw new Error( + `Unsupported union member type for param "${arg.name}, found: ${realTypeAnnotation.memberType}"`, + ); + } case 'ObjectTypeAnnotation': return wrap(val => `${val}.asObject(rt)`); case 'MixedTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js index 1f518d3f47e0f9..6e5f97a8551a39 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleH.js @@ -148,6 +148,17 @@ function translatePrimitiveJSTypeToCpp( return wrap('bool'); case 'GenericObjectTypeAnnotation': return wrap('jsi::Object'); + case 'UnionTypeAnnotation': + switch (typeAnnotation.memberType) { + case 'NumberTypeAnnotation': + return wrap('double'); + case 'ObjectTypeAnnotation': + return wrap('jsi::Object'); + case 'StringTypeAnnotation': + return wrap('jsi::String'); + default: + throw new Error(createErrorMessage(realTypeAnnotation.type)); + } case 'ObjectTypeAnnotation': return wrap('jsi::Object'); case 'ArrayTypeAnnotation': diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js index 538371a7c49520..aa833afbba30c3 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJavaSpec.js @@ -156,7 +156,7 @@ function translateFunctionParamToJavaType( imports.add('com.facebook.react.bridge.Callback'); return 'Callback'; default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error(createErrorMessage(realTypeAnnotation.type)); } } @@ -220,7 +220,7 @@ function translateFunctionReturnTypeToJavaType( imports.add('com.facebook.react.bridge.WritableArray'); return wrapIntoNullableIfNeeded('WritableArray'); default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error(createErrorMessage(realTypeAnnotation.type)); } } @@ -272,7 +272,7 @@ function getFalsyReturnStatementFromReturnType( case 'ArrayTypeAnnotation': return 'return null;'; default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error(createErrorMessage(realTypeAnnotation.type)); } } diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js index 4adb37c943c56d..bca66c4f9925a4 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleJniCpp.js @@ -171,7 +171,7 @@ function translateReturnTypeToKind( case 'ArrayTypeAnnotation': return 'ArrayKind'; default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error( `Unknown prop type for returning value, found: ${realTypeAnnotation.type}"`, ); @@ -226,7 +226,7 @@ function translateParamTypeToJniType( case 'FunctionTypeAnnotation': return 'Lcom/facebook/react/bridge/Callback;'; default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error( `Unknown prop type for method arg, found: ${realTypeAnnotation.type}"`, ); @@ -278,7 +278,7 @@ function translateReturnTypeToJniType( case 'ArrayTypeAnnotation': return 'Lcom/facebook/react/bridge/WritableArray;'; default: - (realTypeAnnotation.type: 'MixedTypeAnnotation'); + (realTypeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error( `Unknown prop type for method return type, found: ${realTypeAnnotation.type}"`, ); diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js index e942ff3c978255..82e8d5bc245d18 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/StructCollector.js @@ -114,6 +114,8 @@ class StructCollector { } case 'MixedTypeAnnotation': throw new Error('Mixed types are unsupported in structs'); + case 'UnionTypeAnnotation': + throw new Error('Union types are unsupported in structs'); default: { return wrapNullable(nullable, typeAnnotation); } diff --git a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js index a74367dcb87197..d7f33cecb7afcd 100644 --- a/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js +++ b/packages/react-native-codegen/src/generators/modules/GenerateModuleObjCpp/serializeMethod.js @@ -338,7 +338,7 @@ function getReturnObjCType( case 'GenericObjectTypeAnnotation': return wrapIntoNullableIfNeeded('NSDictionary *'); default: - (typeAnnotation.type: 'MixedTypeAnnotation'); + (typeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error( `Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, ); @@ -378,7 +378,7 @@ function getReturnJSType( case 'GenericObjectTypeAnnotation': return 'ObjectKind'; default: - (typeAnnotation.type: 'MixedTypeAnnotation'); + (typeAnnotation.type: 'MixedTypeAnnotation' | 'UnionTypeAnnotation'); throw new Error( `Unsupported return type for ${methodName}. Found: ${typeAnnotation.type}`, ); diff --git a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js index 16f6bd517ba44d..590d3e939de5ca 100644 --- a/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/generators/modules/__test_fixtures__/fixtures.js @@ -1567,6 +1567,51 @@ const CXX_ONLY_NATIVE_MODULES: SchemaType = { ], }, }, + { + name: 'getUnion', + optional: false, + typeAnnotation: { + type: 'FunctionTypeAnnotation', + returnTypeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + params: [ + { + name: 'chooseInt', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseFloat', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'NumberTypeAnnotation', + }, + }, + { + name: 'chooseObject', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'ObjectTypeAnnotation', + }, + }, + { + name: 'chooseString', + optional: false, + typeAnnotation: { + type: 'UnionTypeAnnotation', + memberType: 'StringTypeAnnotation', + }, + }, + ], + }, + }, ], }, moduleNames: ['SampleTurboModuleCxx'], diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap index 93a57fc91bcb50..741eb73ee239ed 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleCpp-test.js.snap @@ -115,11 +115,15 @@ static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableNu auto result = static_cast(&turboModule)->getNullableNumberFromNullableAlias(rt, args[0].isNull() || args[0].isUndefined() ? std::nullopt : std::make_optional(args[0].asObject(rt))); return result ? jsi::Value(std::move(*result)) : jsi::Value::null(); } +static jsi::Value __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) { + return static_cast(&turboModule)->getUnion(rt, args[0].asNumber(), args[1].asNumber(), args[2].asObject(rt), args[3].asString(rt)); +} NativeSampleTurboModuleCxxSpecJSI::NativeSampleTurboModuleCxxSpecJSI(std::shared_ptr jsInvoker) : TurboModule(\\"SampleTurboModuleCxx\\", jsInvoker) { methodMap_[\\"getMixed\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getMixed}; methodMap_[\\"getNullableNumberFromNullableAlias\\"] = MethodMetadata {1, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getNullableNumberFromNullableAlias}; + methodMap_[\\"getUnion\\"] = MethodMetadata {4, __hostFunction_NativeSampleTurboModuleCxxSpecJSI_getUnion}; } diff --git a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap index 7fce7ea931084a..8cf7d8ee7f5315 100644 --- a/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap +++ b/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleH-test.js.snap @@ -209,6 +209,7 @@ protected: public: virtual jsi::Value getMixed(jsi::Runtime &rt, jsi::Value arg) = 0; virtual std::optional getNullableNumberFromNullableAlias(jsi::Runtime &rt, std::optional a) = 0; + virtual jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString) = 0; }; @@ -246,6 +247,14 @@ private: return bridging::callFromJs>( rt, &T::getNullableNumberFromNullableAlias, jsInvoker_, instance_, std::move(a)); } + jsi::Object getUnion(jsi::Runtime &rt, double chooseInt, double chooseFloat, jsi::Object chooseObject, jsi::String chooseString) override { + static_assert( + bridging::getParameterCount(&T::getUnion) == 5, + \\"Expected getUnion(...) to have 5 parameters\\"); + + return bridging::callFromJs( + rt, &T::getUnion, jsInvoker_, instance_, std::move(chooseInt), std::move(chooseFloat), std::move(chooseObject), std::move(chooseString)); + } private: T *instance_; diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js index cbbc156c29f97c..f9f84ee20ac15b 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/__test_fixtures__/fixtures.js @@ -593,9 +593,15 @@ const CXX_ONLY_NATIVE_MODULE = ` import type {TurboModule} from '../RCTExport'; import * as TurboModuleRegistry from '../TurboModuleRegistry'; +export type ChooseInt = 1 | 2 | 3; +export type ChooseFloat = 1.44 | 2.88 | 5.76; +export type ChooseObject = {} | {low: string}; +export type ChooseString = 'One' | 'Two' | 'Three'; + export interface Spec extends TurboModule { +getCallback: () => () => void; +getMixed: (arg: mixed) => mixed; + +getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject; } export default TurboModuleRegistry.getEnforcing('SampleTurboModuleCxx'); diff --git a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap index aef18a0ef6c783..abe2c393988318 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/flow/modules/__tests__/__snapshots__/module-parser-snapshot-test.js.snap @@ -77,6 +77,51 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`] } ] } + }, + { + 'name': 'getUnion', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'ObjectTypeAnnotation' + }, + 'params': [ + { + 'name': 'chooseInt', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'NumberTypeAnnotation' + } + }, + { + 'name': 'chooseFloat', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'NumberTypeAnnotation' + } + }, + { + 'name': 'chooseObject', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'ObjectTypeAnnotation' + } + }, + { + 'name': 'chooseString', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'StringTypeAnnotation' + } + } + ] + } } ] }, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/errors.js b/packages/react-native-codegen/src/parsers/flow/modules/errors.js index c8bb4253885e66..03bc6d63be7b08 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/errors.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/errors.js @@ -227,6 +227,30 @@ class UnsupportedFunctionReturnTypeAnnotationParserError extends ParserError { } } +/** + * Union parsing errors + */ + +class UnsupportedUnionTypeAnnotationParserError extends ParserError { + constructor( + hasteModuleName: string, + arrayElementTypeAST: $FlowFixMe, + types: string[], + ) { + super( + hasteModuleName, + arrayElementTypeAST, + `Union members must be of the same type, but multiple types were found ${types.join( + ', ', + )}'.`, + ); + } +} + +/** + * Module parsing errors + */ + class UnusedModuleFlowInterfaceParserError extends ParserError { constructor(hasteModuleName: string, flowInterface: $FlowFixMe) { super( @@ -323,6 +347,7 @@ module.exports = { UnsupportedFlowTypeAnnotationParserError, UnsupportedFunctionParamTypeAnnotationParserError, UnsupportedFunctionReturnTypeAnnotationParserError, + UnsupportedUnionTypeAnnotationParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, diff --git a/packages/react-native-codegen/src/parsers/flow/modules/index.js b/packages/react-native-codegen/src/parsers/flow/modules/index.js index 38ce80db4e746c..528813922cf87f 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -44,6 +44,7 @@ const { UnsupportedFlowTypeAnnotationParserError, UnsupportedFunctionParamTypeAnnotationParserError, UnsupportedFunctionReturnTypeAnnotationParserError, + UnsupportedUnionTypeAnnotationParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, @@ -367,6 +368,33 @@ function translateTypeAnnotation( ), ); } + case 'UnionTypeAnnotation': { + if (cxxOnly) { + // Remap literal names + const unionTypes = typeAnnotation.types + .map( + item => + item.type + .replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation') + .replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation'), + // ObjectAnnotation is already 'correct' + ) + .filter((value, index, self) => self.indexOf(value) === index); + // Only support unionTypes of the same kind + if (unionTypes.length > 1) { + throw new UnsupportedUnionTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + unionTypes, + ); + } + return wrapNullable(nullable, { + type: 'UnionTypeAnnotation', + memberType: unionTypes[0], + }); + } + // Fallthrough + } case 'MixedTypeAnnotation': { if (cxxOnly) { return wrapNullable(nullable, { diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js index 39956142c70e5e..f776e26ae3ca62 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__test_fixtures__/fixtures.js @@ -608,9 +608,15 @@ const CXX_ONLY_NATIVE_MODULE = ` import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport'; import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry'; +export type ChooseInt = 1 | 2 | 3; +export type ChooseFloat = 1.44 | 2.88 | 5.76; +export type ChooseObject = {} | {low: string}; +export type ChooseString = 'One' | 'Two' | 'Three'; + export interface Spec extends TurboModule { readonly getCallback: () => () => void; readonly getMixed: (arg: unknown) => unknown; + readonly getUnion: (chooseInt: ChooseInt, chooseFloat: ChooseFloat, chooseObject: ChooseObject, chooseString: ChooseString) => ChooseObject; } export default TurboModuleRegistry.getEnforcing( diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap index 20420a83361108..e56dfc7871c8ad 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap +++ b/packages/react-native-codegen/src/parsers/typescript/modules/__tests__/__snapshots__/typescript-module-parser-snapshot-test.js.snap @@ -75,6 +75,51 @@ exports[`RN Codegen TypeScript Parser can generate fixture CXX_ONLY_NATIVE_MODUL } ] } + }, + { + 'name': 'getUnion', + 'optional': false, + 'typeAnnotation': { + 'type': 'FunctionTypeAnnotation', + 'returnTypeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'ObjectTypeAnnotation' + }, + 'params': [ + { + 'name': 'chooseInt', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'NumberTypeAnnotation' + } + }, + { + 'name': 'chooseFloat', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'NumberTypeAnnotation' + } + }, + { + 'name': 'chooseObject', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'ObjectTypeAnnotation' + } + }, + { + 'name': 'chooseString', + 'optional': false, + 'typeAnnotation': { + 'type': 'UnionTypeAnnotation', + 'memberType': 'StringTypeAnnotation' + } + } + ] + } } ] }, @@ -369,7 +414,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR }" `; -exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS 1`] = ` +exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE 1`] = ` "{ 'modules': { 'NativeSampleTurboModule': { @@ -383,20 +428,14 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'typeAnnotation': { 'type': 'FunctionTypeAnnotation', 'returnTypeAnnotation': { - 'type': 'ArrayTypeAnnotation', - 'elementType': { - 'type': 'StringTypeAnnotation' - } + 'type': 'ArrayTypeAnnotation' }, 'params': [ { 'name': 'arg', 'optional': false, 'typeAnnotation': { - 'type': 'ArrayTypeAnnotation', - 'elementType': { - 'type': 'StringTypeAnnotation' - } + 'type': 'ArrayTypeAnnotation' } } ] @@ -412,7 +451,7 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR }" `; -exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WITH_UNION_AND_TOUPLE 1`] = ` +exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS 1`] = ` "{ 'modules': { 'NativeSampleTurboModule': { @@ -426,14 +465,20 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_AR 'typeAnnotation': { 'type': 'FunctionTypeAnnotation', 'returnTypeAnnotation': { - 'type': 'ArrayTypeAnnotation' + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } }, 'params': [ { 'name': 'arg', 'optional': false, 'typeAnnotation': { - 'type': 'ArrayTypeAnnotation' + 'type': 'ArrayTypeAnnotation', + 'elementType': { + 'type': 'StringTypeAnnotation' + } } } ] diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/errors.js b/packages/react-native-codegen/src/parsers/typescript/modules/errors.js index 357135d787578f..76064b5f0566af 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/errors.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/errors.js @@ -223,6 +223,30 @@ class UnsupportedFunctionReturnTypeAnnotationParserError extends ParserError { } } +/** + * Union parsing errors + */ + +class UnsupportedTypeScriptUnionTypeAnnotationParserError extends ParserError { + constructor( + hasteModuleName: string, + arrayElementTypeAST: $FlowFixMe, + types: string[], + ) { + super( + hasteModuleName, + arrayElementTypeAST, + `Union members must be of the same type, but multiple types were found ${types.join( + ', ', + )}'.`, + ); + } +} + +/** + * Module parsing errors + */ + class UnusedModuleTypeScriptInterfaceParserError extends ParserError { constructor(hasteModuleName: string, flowInterface: $FlowFixMe) { super( @@ -319,6 +343,7 @@ module.exports = { UnsupportedTypeScriptTypeAnnotationParserError, UnsupportedFunctionParamTypeAnnotationParserError, UnsupportedFunctionReturnTypeAnnotationParserError, + UnsupportedTypeScriptUnionTypeAnnotationParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, diff --git a/packages/react-native-codegen/src/parsers/typescript/modules/index.js b/packages/react-native-codegen/src/parsers/typescript/modules/index.js index d0e395e31ac007..5db27cc5f9559d 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -44,6 +44,7 @@ const { UnsupportedTypeScriptTypeAnnotationParserError, UnsupportedFunctionParamTypeAnnotationParserError, UnsupportedFunctionReturnTypeAnnotationParserError, + UnsupportedTypeScriptUnionTypeAnnotationParserError, UnsupportedModulePropertyParserError, UnsupportedObjectPropertyTypeAnnotationParserError, UnsupportedObjectPropertyValueTypeAnnotationParserError, @@ -419,6 +420,33 @@ function translateTypeAnnotation( ), ); } + case 'TSUnionType': { + if (cxxOnly) { + // Remap literal names + const unionTypes = typeAnnotation.types + .map(item => + item.literal + ? item.literal.type + .replace('NumericLiteral', 'NumberTypeAnnotation') + .replace('StringLiteral', 'StringTypeAnnotation') + : 'ObjectTypeAnnotation', + ) + .filter((value, index, self) => self.indexOf(value) === index); + // Only support unionTypes of the same kind + if (unionTypes.length > 1) { + throw new UnsupportedTypeScriptUnionTypeAnnotationParserError( + hasteModuleName, + typeAnnotation, + unionTypes, + ); + } + return wrapNullable(nullable, { + type: 'UnionTypeAnnotation', + memberType: unionTypes[0], + }); + } + // Fallthrough + } case 'TSUnknownKeyword': { if (cxxOnly) { return wrapNullable(nullable, {