From bf34810c5c188cd1c42e2ac0c52d08790209bc1e Mon Sep 17 00:00:00 2001 From: "Zihan Chen (MSFT)" <53799235+ZihanChen-MSFT@users.noreply.github.com> Date: Tue, 31 Jan 2023 12:05:46 -0800 Subject: [PATCH] Turbo module codegen support interface with inheritance in module (#36011) Summary: The [previous pull request](https://github.com/facebook/react-native/pull/35812) enables defining interfaces and using them in a turbo module, but all members are flattened, this is not the best option for codegen for object oriented languages. In this pull request, an optional member `baseTypes` is added to aliased objects. Members are still flattened for backward compatibility, if a codegen doesn't care about that nothing needs to be changed. ### Example ```typescript interface A { a : string; } interface B extends A { b : number; } ``` #### Before the previous pull request `interface` is not allowed here, you must use type alias. #### At the previous pull request `interface` is allowed, but it is translated to ```typescript type A = {a : string}; type B = {a : string, b : number}; ``` #### At this pull request Extra information is provided so that you know `B` extends `A`. By comparing `B` to `A` it is easy to know that `B::a` is obtained from `A`. ## Changelog [GENERAL] [CHANGED] - Turbo module codegen support interface with inheritance in module Pull Request resolved: https://github.com/facebook/react-native/pull/36011 Test Plan: `yarn jest react-native-codegen` passed Reviewed By: rshest Differential Revision: D42882650 Pulled By: cipolleschi fbshipit-source-id: 32d502e8a99c4151fae0a1f4dfa60db9ac827963 --- .../react-native-codegen/src/CodegenSchema.js | 3 ++ ...script-module-parser-snapshot-test.js.snap | 54 +++++++++++++++++++ .../src/parsers/typescript/modules/index.js | 28 +++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/packages/react-native-codegen/src/CodegenSchema.js b/packages/react-native-codegen/src/CodegenSchema.js index 8033695ba65513..4a16783ef0fff3 100644 --- a/packages/react-native-codegen/src/CodegenSchema.js +++ b/packages/react-native-codegen/src/CodegenSchema.js @@ -53,6 +53,9 @@ export type VoidTypeAnnotation = $ReadOnly<{ export type ObjectTypeAnnotation<+T> = $ReadOnly<{ type: 'ObjectTypeAnnotation', properties: $ReadOnlyArray>, + + // metadata for objects that generated from interfaces + baseTypes?: $ReadOnlyArray, }>; type FunctionTypeAnnotation<+P, +R> = $ReadOnly<{ 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 d6f4f9d4e3679e..38fd3e1186724a 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 @@ -1521,6 +1521,56 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NE } ] }, + 'Base1': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'bar1', + 'optional': false, + 'typeAnnotation': { + 'type': 'TypeAliasTypeAnnotation', + 'name': 'Bar' + } + } + ] + }, + 'Base2': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'bar2', + 'optional': false, + 'typeAnnotation': { + 'type': 'TypeAliasTypeAnnotation', + 'name': 'Bar' + } + } + ] + }, + 'Base3': { + 'type': 'ObjectTypeAnnotation', + 'properties': [ + { + 'name': 'bar2', + 'optional': false, + 'typeAnnotation': { + 'type': 'TypeAliasTypeAnnotation', + 'name': 'Bar' + } + }, + { + 'name': 'bar3', + 'optional': false, + 'typeAnnotation': { + 'type': 'TypeAliasTypeAnnotation', + 'name': 'Bar' + } + } + ], + 'baseTypes': [ + 'Base2' + ] + }, 'Foo': { 'type': 'ObjectTypeAnnotation', 'properties': [ @@ -1556,6 +1606,10 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NE 'name': 'Bar' } } + ], + 'baseTypes': [ + 'Base1', + 'Base3' ] } }, 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 1d0a222b48d828..74331eaeb81b8a 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -221,7 +221,26 @@ function translateTypeAnnotation( } } case 'TSInterfaceDeclaration': { - const objectTypeAnnotation = { + const baseTypes = (typeAnnotation.extends ?? []).map( + extend => extend.expression.name, + ); + for (const baseType of baseTypes) { + // ensure base types exist and appear in aliasMap + translateTypeAnnotation( + hasteModuleName, + { + type: 'TSTypeReference', + typeName: {type: 'Identifier', name: baseType}, + }, + types, + aliasMap, + tryParse, + cxxOnly, + parser, + ); + } + + let objectTypeAnnotation = { type: 'ObjectTypeAnnotation', // $FlowFixMe[missing-type-arg] properties: (flattenProperties( @@ -246,8 +265,15 @@ function translateTypeAnnotation( }, ) .filter(Boolean), + baseTypes, }; + if (objectTypeAnnotation.baseTypes.length === 0) { + // The flow checker does not allow adding a member after an object literal is created + // so here I do it in a reverse way + delete objectTypeAnnotation.baseTypes; + } + return typeAliasResolution( typeAliasResolutionStatus, objectTypeAnnotation,