Skip to content

Commit e948c9d

Browse files
Support ReadOnly/Readonly wrapped custom structs in C++ TurboModule codegen
Summary: When parsing custom struct types in RN TurboModule flow specs for C++ (cxxOnly) codegen, the `getObjectTypeAnnotations` function in `parsers-commons.js` silently dropped types wrapped in `$ReadOnly<{...}>` or `Readonly<{...}>`. The root cause: `parser.nextNodeForTypeAlias(value)` returns the raw right-hand side of a type alias. For `export type Foo = Readonly<{...}>`, this returns a `GenericTypeAnnotation` node (the `Readonly<...>` wrapper), not an `ObjectTypeAnnotation`. The existing guard check (`parent.type !== 'ObjectTypeAnnotation'`) then rejects the type, causing it to be silently skipped from the alias map. The fix unwraps `$ReadOnly`/`Readonly` `GenericTypeAnnotation` wrappers (Flow) and `Readonly` `TSTypeReference` wrappers (TypeScript) before performing the type check, then obtains properties directly from the unwrapped node. Also updates `NativeCxxModuleExample.js` to use `Readonly<{...}>` on `ConstantsStruct` as a demonstration and build-time validation of the fix. Changelog: [Internal] Differential Revision: D96044884
1 parent 63800af commit e948c9d

File tree

2 files changed

+32
-20
lines changed

2 files changed

+32
-20
lines changed

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

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -189,30 +189,42 @@ function getObjectTypeAnnotations(
189189
if (!isTypeAlias) {
190190
return;
191191
}
192-
const parent = parser.nextNodeForTypeAlias(value);
192+
let parent = parser.nextNodeForTypeAlias(value);
193+
// Unwrap $ReadOnly/Readonly wrappers to get the inner object type
194+
if (
195+
parent.type === 'GenericTypeAnnotation' &&
196+
(parent.id?.name === '$ReadOnly' || parent.id?.name === 'Readonly') &&
197+
parent.typeParameters?.params?.length === 1
198+
) {
199+
parent = parent.typeParameters.params[0];
200+
} else if (
201+
parent.type === 'TSTypeReference' &&
202+
parent.typeName?.name === 'Readonly' &&
203+
parent.typeParameters?.params?.length === 1
204+
) {
205+
parent = parent.typeParameters.params[0];
206+
}
193207
if (
194208
parent.type !== 'ObjectTypeAnnotation' &&
195209
parent.type !== 'TSTypeLiteral'
196210
) {
197211
return;
198212
}
199-
const typeProperties = parser
200-
.getAnnotatedElementProperties(value)
201-
.map(prop =>
202-
parseObjectProperty(
203-
parent,
204-
prop,
205-
hasteModuleName,
206-
types,
207-
aliasMap,
208-
{}, // enumMap
209-
tryParse,
210-
true, // cxxOnly
211-
prop?.optional || false,
212-
translateTypeAnnotation,
213-
parser,
214-
),
215-
);
213+
const typeProperties = (parent.properties ?? parent.members).map(prop =>
214+
parseObjectProperty(
215+
parent,
216+
prop,
217+
hasteModuleName,
218+
types,
219+
aliasMap,
220+
{}, // enumMap
221+
tryParse,
222+
true, // cxxOnly
223+
prop?.optional || false,
224+
translateTypeAnnotation,
225+
parser,
226+
),
227+
);
216228
aliasMap[key] = {
217229
type: 'ObjectTypeAnnotation',
218230
properties: typeProperties,

packages/rn-tester/NativeCxxModuleExample/NativeCxxModuleExample.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ export type UnionFloat = 1.44 | 2.88 | 5.76;
3131
export type UnionString = 'One' | 'Two' | 'Three';
3232
export type UnionObject = {value: number} | {low: string};
3333

34-
export type ConstantsStruct = {
34+
export type ConstantsStruct = Readonly<{
3535
const1: boolean,
3636
const2: number,
3737
const3: string,
38-
};
38+
}>;
3939

4040
export type ObjectStruct = {
4141
a: number,

0 commit comments

Comments
 (0)