diff --git a/packages/react-native-codegen/src/CodegenSchema.d.ts b/packages/react-native-codegen/src/CodegenSchema.d.ts
index fc13854cb0d..e3487768b66 100644
--- a/packages/react-native-codegen/src/CodegenSchema.d.ts
+++ b/packages/react-native-codegen/src/CodegenSchema.d.ts
@@ -60,7 +60,7 @@ export interface MixedTypeAnnotation {
export interface EventEmitterTypeAnnotation {
readonly type: 'EventEmitterTypeAnnotation';
- readonly typeAnnotation: NativeModuleBaseTypeAnnotation;
+ readonly typeAnnotation: NativeModuleEventEmitterTypeAnnotation;
}
export interface FunctionTypeAnnotation
{
@@ -360,6 +360,23 @@ export interface NativeModuleMixedTypeAnnotation {
readonly type: 'MixedTypeAnnotation';
}
+export type NativeModuleEventEmitterBaseTypeAnnotation =
+ | NativeModuleBooleanTypeAnnotation
+ | NativeModuleDoubleTypeAnnotation
+ | NativeModuleFloatTypeAnnotation
+ | NativeModuleInt32TypeAnnotation
+ | NativeModuleNumberTypeAnnotation
+ | NativeModuleStringTypeAnnotation
+ | NativeModuleTypeAliasTypeAnnotation
+ | VoidTypeAnnotation;
+
+export type NativeModuleEventEmitterTypeAnnotation =
+ | NativeModuleEventEmitterBaseTypeAnnotation
+ | {
+ readonly type: 'ArrayTypeAnnotation';
+ readonly elementType: NativeModuleEventEmitterBaseTypeAnnotation | {type: string};
+ };
+
export type NativeModuleBaseTypeAnnotation =
| NativeModuleStringTypeAnnotation
| NativeModuleNumberTypeAnnotation
@@ -387,7 +404,8 @@ export type NativeModuleReturnTypeAnnotation =
export type NativeModuleTypeAnnotation =
| NativeModuleBaseTypeAnnotation
| NativeModuleParamOnlyTypeAnnotation
- | NativeModuleReturnOnlyTypeAnnotation;
+ | NativeModuleReturnOnlyTypeAnnotation
+ | NativeModuleEventEmitterTypeAnnotation;
type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation;
diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js
index d53c3942159..3d13164dc6e 100644
--- a/packages/react-native-codegen/src/CodegenSchema.js
+++ b/packages/react-native-codegen/src/CodegenSchema.js
@@ -63,7 +63,7 @@ export type MixedTypeAnnotation = $ReadOnly<{
type EventEmitterTypeAnnotation = $ReadOnly<{
type: 'EventEmitterTypeAnnotation',
- typeAnnotation: NativeModuleBaseTypeAnnotation,
+ typeAnnotation: NativeModuleEventEmitterTypeAnnotation | $FlowFixMe,
}>;
type FunctionTypeAnnotation<+P, +R> = $ReadOnly<{
@@ -366,6 +366,23 @@ export type NativeModuleMixedTypeAnnotation = $ReadOnly<{
type: 'MixedTypeAnnotation',
}>;
+type NativeModuleEventEmitterBaseTypeAnnotation =
+ | NativeModuleBooleanTypeAnnotation
+ | NativeModuleDoubleTypeAnnotation
+ | NativeModuleFloatTypeAnnotation
+ | NativeModuleInt32TypeAnnotation
+ | NativeModuleNumberTypeAnnotation
+ | NativeModuleStringTypeAnnotation
+ | NativeModuleTypeAliasTypeAnnotation
+ | VoidTypeAnnotation;
+
+export type NativeModuleEventEmitterTypeAnnotation =
+ | NativeModuleEventEmitterBaseTypeAnnotation
+ | {
+ type: 'ArrayTypeAnnotation',
+ elementType: NativeModuleEventEmitterBaseTypeAnnotation | {type: string},
+ };
+
export type NativeModuleBaseTypeAnnotation =
| NativeModuleStringTypeAnnotation
| NativeModuleNumberTypeAnnotation
@@ -393,7 +410,8 @@ export type NativeModuleReturnTypeAnnotation =
export type NativeModuleTypeAnnotation =
| NativeModuleBaseTypeAnnotation
| NativeModuleParamOnlyTypeAnnotation
- | NativeModuleReturnOnlyTypeAnnotation;
+ | NativeModuleReturnOnlyTypeAnnotation
+ | NativeModuleEventEmitterTypeAnnotation;
type NativeModuleParamOnlyTypeAnnotation = NativeModuleFunctionTypeAnnotation;
diff --git a/packages/react-native-codegen/src/parsers/errors.js b/packages/react-native-codegen/src/parsers/errors.js
index dde9cf45ac5..101138a7e10 100644
--- a/packages/react-native-codegen/src/parsers/errors.js
+++ b/packages/react-native-codegen/src/parsers/errors.js
@@ -107,7 +107,7 @@ class UnsupportedModuleEventEmitterPropertyParserError extends ParserError {
message += `'${propertyValue}' must non nullable.`;
} else if (untyped) {
message += `'${propertyValue}' must have a concrete or void eventType.`;
- } else if (cxxOnly) {
+ } else if (!cxxOnly) {
message += `'${propertyValue}' is only supported in C++ Turbo Modules.`;
}
super(nativeModuleName, propertyValue, message);
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 534a2ceedb7..4b1efc38eed 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
@@ -620,6 +620,42 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule');
`;
+const NATIVE_MODULE_WITH_EVENT_EMITTERS = `
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @flow strict-local
+ * @format
+ */
+
+'use strict';
+
+import type {TurboModule} from '../RCTExport';
+import type {EventEmitter} from '../CodegenTypes';
+import * as TurboModuleRegistry from '../TurboModuleRegistry';
+
+export type ObjectStruct = {
+ a: number,
+ b: string,
+ c?: ?string,
+};
+
+export interface Spec extends TurboModule {
+ +onEvent1: EventEmitter;
+ +onEvent2: EventEmitter;
+ +onEvent3: EventEmitter;
+ +onEvent4: EventEmitter;
+ +onEvent5: EventEmitter;
+ +onEvent6: EventEmitter;
+}
+
+export default TurboModuleRegistry.getEnforcing('SampleTurboModuleCxx');
+
+`;
+
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -813,6 +849,7 @@ module.exports = {
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
+ NATIVE_MODULE_WITH_EVENT_EMITTERS,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
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 e7e7dc4a559..17684eeea46 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
@@ -1424,6 +1424,123 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
}"
`;
+exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_EVENT_EMITTERS 1`] = `
+"{
+ 'modules': {
+ 'NativeSampleTurboModule': {
+ 'type': 'NativeModule',
+ 'aliasMap': {
+ 'ObjectStruct': {
+ 'type': 'ObjectTypeAnnotation',
+ 'properties': [
+ {
+ 'name': 'a',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'NumberTypeAnnotation'
+ }
+ },
+ {
+ 'name': 'b',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ },
+ {
+ 'name': 'c',
+ 'optional': true,
+ 'typeAnnotation': {
+ 'type': 'NullableTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ }
+ }
+ ]
+ }
+ },
+ 'enumMap': {},
+ 'spec': {
+ 'eventEmitters': [
+ {
+ 'name': 'onEvent1',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'VoidTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent2',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent3',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'NumberTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent4',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'BooleanTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent5',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'TypeAliasTypeAnnotation',
+ 'name': 'ObjectStruct'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent6',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'ArrayTypeAnnotation',
+ 'elementType': {
+ 'type': 'TypeAliasTypeAnnotation',
+ 'name': 'ObjectStruct'
+ }
+ }
+ }
+ }
+ ],
+ 'methods': []
+ },
+ 'moduleName': 'SampleTurboModuleCxx',
+ 'excludedPlatforms': [
+ 'iOS',
+ 'android'
+ ]
+ }
+ }
+}"
+`;
+
exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_FLOAT_AND_INT32 1`] = `
"{
'modules': {
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 d08345353a3..adfea71f5b1 100644
--- a/packages/react-native-codegen/src/parsers/flow/modules/index.js
+++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js
@@ -40,6 +40,7 @@ const {
emitPromise,
emitRootTag,
emitUnion,
+ translateArrayTypeAnnotation,
typeAliasResolution,
typeEnumResolution,
} = require('../../parsers-primitives');
@@ -62,6 +63,20 @@ function translateTypeAnnotation(
resolveTypeAnnotationFN(flowTypeAnnotation, types, parser);
switch (typeAnnotation.type) {
+ case 'ArrayTypeAnnotation': {
+ return translateArrayTypeAnnotation(
+ hasteModuleName,
+ types,
+ aliasMap,
+ enumMap,
+ cxxOnly,
+ 'Array',
+ typeAnnotation.elementType,
+ nullable,
+ translateTypeAnnotation,
+ parser,
+ );
+ }
case 'GenericTypeAnnotation': {
switch (parser.getTypeAnnotationName(typeAnnotation)) {
case 'RootTag': {
diff --git a/packages/react-native-codegen/src/parsers/parsers-commons.js b/packages/react-native-codegen/src/parsers/parsers-commons.js
index 443fc7dff71..f012943eade 100644
--- a/packages/react-native-codegen/src/parsers/parsers-commons.js
+++ b/packages/react-native-codegen/src/parsers/parsers-commons.js
@@ -489,15 +489,22 @@ function buildEventEmitterSchema(
translateTypeAnnotation: $FlowFixMe,
parser: Parser,
): NativeModuleEventEmitterShape {
- let {key, value} = property;
- const eventemitterName: string = key.name;
+ const {key} = property;
+ const value =
+ parser.language() === 'TypeScript'
+ ? property.typeAnnotation.typeAnnotation
+ : property.value;
+ const eventemitterName: string = key.name;
const resolveTypeAnnotationFN = parser.getResolveTypeAnnotationFN();
const [typeAnnotation, typeAnnotationNullable] = unwrapNullable(value);
const typeAnnotationUntyped =
value.typeParameters.params.length === 1 &&
- value.typeParameters.params[0].type === 'ObjectTypeAnnotation' &&
- value.typeParameters.params[0].properties.length === 0;
+ parser.language() === 'TypeScript'
+ ? value.typeParameters.params[0].type === 'TSTypeLiteral' &&
+ value.typeParameters.params[0].members.length === 0
+ : value.typeParameters.params[0].type === 'ObjectTypeAnnotation' &&
+ value.typeParameters.params[0].properties.length === 0;
throwIfEventEmitterTypeIsUnsupported(
hasteModuleName,
@@ -520,12 +527,24 @@ function buildEventEmitterSchema(
parser,
eventTypeResolutionStatus.nullable,
);
+
+ const eventTypeAnnotation = translateTypeAnnotation(
+ hasteModuleName,
+ typeAnnotation.typeParameters.params[0],
+ types,
+ aliasMap,
+ enumMap,
+ tryParse,
+ cxxOnly,
+ parser,
+ );
+
return {
name: eventemitterName,
optional: false,
typeAnnotation: {
type: 'EventEmitterTypeAnnotation',
- typeAnnotation: {type: eventTypeResolutionStatus.typeAnnotation.type},
+ typeAnnotation: eventTypeAnnotation,
},
};
}
@@ -775,7 +794,6 @@ const buildModuleSchema = (
parser,
)
: {};
-
const properties: $ReadOnlyArray<$FlowFixMe> =
language === 'Flow' ? moduleSpec.body.properties : moduleSpec.body.body;
@@ -798,8 +816,12 @@ const buildModuleSchema = (
}>(property => {
const enumMap: {...NativeModuleEnumMap} = {};
const isEventEmitter =
- property?.value?.type === 'GenericTypeAnnotation' &&
- property?.value?.id?.name === 'EventEmitter';
+ language === 'TypeScript'
+ ? property?.type === 'TSPropertySignature' &&
+ property?.typeAnnotation?.typeAnnotation?.typeName?.name ===
+ 'EventEmitter'
+ : property?.value?.type === 'GenericTypeAnnotation' &&
+ property?.value?.id?.name === 'EventEmitter';
return tryParse(() => ({
aliasMap,
enumMap,
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 453e1891440..56a3a87a074 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
@@ -723,6 +723,38 @@ export default TurboModuleRegistry.getEnforcing('SampleTurboModule');
`;
+const NATIVE_MODULE_WITH_EVENT_EMITTERS = `
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ */
+
+import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
+import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
+
+export type ObjectStruct = {
+ a: number;
+ b: string;
+ c?: string | null;
+};
+
+export interface Spec extends TurboModule {
+ readonly onEvent1: EventEmitter;
+ readonly onEvent2: EventEmitter;
+ readonly onEvent3: EventEmitter;
+ readonly onEvent4: EventEmitter;
+ readonly onEvent5: EventEmitter;
+ readonly onEvent6: EventEmitter;
+}
+
+export default TurboModuleRegistry.getEnforcing('SampleTurboModuleCxx');
+
+`;
+
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -875,6 +907,7 @@ module.exports = {
NATIVE_MODULE_WITH_BASIC_PARAM_TYPES,
NATIVE_MODULE_WITH_CALLBACK,
NATIVE_MODULE_WITH_UNION,
+ NATIVE_MODULE_WITH_EVENT_EMITTERS,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
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 4ae381e47a7..6eeda94587b 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
@@ -1624,6 +1624,123 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_CO
}"
`;
+exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_EVENT_EMITTERS 1`] = `
+"{
+ 'modules': {
+ 'NativeSampleTurboModule': {
+ 'type': 'NativeModule',
+ 'aliasMap': {
+ 'ObjectStruct': {
+ 'type': 'ObjectTypeAnnotation',
+ 'properties': [
+ {
+ 'name': 'a',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'NumberTypeAnnotation'
+ }
+ },
+ {
+ 'name': 'b',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ },
+ {
+ 'name': 'c',
+ 'optional': true,
+ 'typeAnnotation': {
+ 'type': 'NullableTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ }
+ }
+ ]
+ }
+ },
+ 'enumMap': {},
+ 'spec': {
+ 'eventEmitters': [
+ {
+ 'name': 'onEvent1',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'VoidTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent2',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'StringTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent3',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'NumberTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent4',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'BooleanTypeAnnotation'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent5',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'TypeAliasTypeAnnotation',
+ 'name': 'ObjectStruct'
+ }
+ }
+ },
+ {
+ 'name': 'onEvent6',
+ 'optional': false,
+ 'typeAnnotation': {
+ 'type': 'EventEmitterTypeAnnotation',
+ 'typeAnnotation': {
+ 'type': 'ArrayTypeAnnotation',
+ 'elementType': {
+ 'type': 'TypeAliasTypeAnnotation',
+ 'name': 'ObjectStruct'
+ }
+ }
+ }
+ }
+ ],
+ 'methods': []
+ },
+ 'moduleName': 'SampleTurboModuleCxx',
+ 'excludedPlatforms': [
+ 'iOS',
+ 'android'
+ ]
+ }
+ }
+}"
+`;
+
exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_FLOAT_AND_INT32 1`] = `
"{
'modules': {
diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js
index 7262b60301b..04f8c1d69e6 100644
--- a/packages/react-native-codegen/src/parsers/typescript/parser.js
+++ b/packages/react-native-codegen/src/parsers/typescript/parser.js
@@ -337,6 +337,8 @@ class TypeScriptParser implements Parser {
convertKeywordToTypeAnnotation(keyword: string): string {
switch (keyword) {
+ case 'TSArrayType':
+ return 'ArrayTypeAnnotation';
case 'TSBooleanKeyword':
return 'BooleanTypeAnnotation';
case 'TSNumberKeyword':
@@ -345,6 +347,8 @@ class TypeScriptParser implements Parser {
return 'VoidTypeAnnotation';
case 'TSStringKeyword':
return 'StringTypeAnnotation';
+ case 'TSTypeLiteral':
+ return 'ObjectTypeAnnotation';
case 'TSUnknownKeyword':
return 'MixedTypeAnnotation';
}