Skip to content

Commit

Permalink
Turbo Module supports intersection type for TypeScript (facebook#36037)
Browse files Browse the repository at this point in the history
Summary:
As [requested from community](reactwg/react-native-new-architecture#91 (comment)), intersection type is supported for component but no in turbo module. This PR fixed it.

## Changelog

[GENERAL] [CHANGED] - Turbo Module supports intersection type for TypeScript

Pull Request resolved: facebook#36037

Test Plan: `yarn jest react-native-codegen` passed

Reviewed By: christophpurrer

Differential Revision: D42960338

Pulled By: cipolleschi

fbshipit-source-id: d317d3155cb96643bf549d0ac3d77f503685edbc
  • Loading branch information
ZihanChen-MSFT authored and OlimpiaZurek committed May 22, 2023
1 parent c1895ae commit 9b24359
Show file tree
Hide file tree
Showing 4 changed files with 255 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const tsExtraCases = [
'NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE',
'NATIVE_MODULE_WITH_BASIC_ARRAY2',
'NATIVE_MODULE_WITH_COMPLEX_ARRAY2',
'NATIVE_MODULE_WITH_INTERSECTION_TYPES',
'NATIVE_MODULE_WITH_NESTED_INTERFACES',
];
const ignoredCases = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,50 @@ export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;

const NATIVE_MODULE_WITH_INTERSECTION_TYPES = `
/**
* 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 '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
type Bar = {
z: number
};
type Base1 = {
bar1: Bar,
}
type Base2 = {
bar2: Bar,
}
type Base3 = Base2 & {
bar3: Bar,
}
type Foo = Base1 & Base3 & {
bar4: Bar,
};
export interface Spec extends TurboModule {
// Exported methods.
foo1: (x: Foo) => Foo;
foo2: (x: Foo) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;

const NATIVE_MODULE_WITH_FLOAT_AND_INT32 = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
Expand Down Expand Up @@ -803,6 +847,7 @@ module.exports = {
NATIVE_MODULE_WITH_ALIASES,
NATIVE_MODULE_WITH_NESTED_ALIASES,
NATIVE_MODULE_WITH_NESTED_INTERFACES,
NATIVE_MODULE_WITH_INTERSECTION_TYPES,
NATIVE_MODULE_WITH_PROMISE,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS,
NATIVE_MODULE_WITH_COMPLEX_OBJECTS_WITH_NULLABLE_KEY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,114 @@ exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_FL
}"
`;

exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_INTERSECTION_TYPES 1`] = `
"{
'modules': {
'NativeSampleTurboModule': {
'type': 'NativeModule',
'aliasMap': {
'Bar': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'z',
'optional': false,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
}
]
},
'Foo': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'bar1',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Bar'
}
},
{
'name': 'bar2',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Bar'
}
},
{
'name': 'bar3',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Bar'
}
},
{
'name': 'bar4',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Bar'
}
}
]
}
},
'enumMap': {},
'spec': {
'properties': [
{
'name': 'foo1',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Foo'
},
'params': [
{
'name': 'x',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Foo'
}
}
]
}
},
{
'name': 'foo2',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'VoidTypeAnnotation'
},
'params': [
{
'name': 'x',
'optional': false,
'typeAnnotation': {
'type': 'TypeAliasTypeAnnotation',
'name': 'Foo'
}
}
]
}
}
]
},
'moduleName': 'SampleTurboModule'
}
}
}"
`;

exports[`RN Codegen TypeScript Parser can generate fixture NATIVE_MODULE_WITH_NESTED_ALIASES 1`] = `
"{
'modules': {
Expand Down
167 changes: 101 additions & 66 deletions packages/react-native-codegen/src/parsers/typescript/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ import type {
} from '../../../CodegenSchema';

import type {Parser} from '../../parser';
import type {ParserErrorCapturer, TypeDeclarationMap} from '../../utils';
import type {
ParserErrorCapturer,
TypeResolutionStatus,
TypeDeclarationMap,
} from '../../utils';
const {flattenIntersectionType} = require('../parseTopLevelType');
const {flattenProperties} = require('../components/componentsUtils');

const {visit, isModuleRegistryCall, verifyPlatforms} = require('../../utils');
Expand Down Expand Up @@ -74,6 +79,64 @@ const {

const language = 'TypeScript';

function translateObjectTypeAnnotation(
hasteModuleName: string,
/**
* TODO(T108222691): Use flow-types for @babel/parser
*/
nullable: boolean,
objectMembers: $ReadOnlyArray<$FlowFixMe>,
typeResolutionStatus: TypeResolutionStatus,
baseTypes: $ReadOnlyArray<string>,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
enumMap: {...NativeModuleEnumMap},
tryParse: ParserErrorCapturer,
cxxOnly: boolean,
parser: Parser,
): Nullable<NativeModuleTypeAnnotation> {
// $FlowFixMe[missing-type-arg]
const properties = objectMembers
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(property => {
return tryParse(() => {
return parseObjectProperty(
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
})
.filter(Boolean);

let objectTypeAnnotation;
if (baseTypes.length === 0) {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
};
} else {
objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties,
baseTypes,
};
}

return typeAliasResolution(
typeResolutionStatus,
objectTypeAnnotation,
aliasMap,
nullable,
);
}

function translateTypeAnnotation(
hasteModuleName: string,
/**
Expand Down Expand Up @@ -246,46 +309,36 @@ function translateTypeAnnotation(
);
}

let objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: (flattenProperties(
[typeAnnotation],
types,
): $ReadOnlyArray<$FlowFixMe>)
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
return parseObjectProperty(
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
},
)
.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(
return translateObjectTypeAnnotation(
hasteModuleName,
nullable,
flattenProperties([typeAnnotation], types),
typeResolutionStatus,
objectTypeAnnotation,
baseTypes,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSIntersectionType': {
return translateObjectTypeAnnotation(
hasteModuleName,
nullable,
flattenProperties(
flattenIntersectionType(typeAnnotation, types),
types,
),
typeResolutionStatus,
[],
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSTypeLiteral': {
Expand Down Expand Up @@ -313,36 +366,18 @@ function translateTypeAnnotation(
}
}

const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
properties: (typeAnnotation.members: Array<$FlowFixMe>)
.map<?NamedShape<Nullable<NativeModuleBaseTypeAnnotation>>>(
property => {
return tryParse(() => {
return parseObjectProperty(
property,
hasteModuleName,
types,
aliasMap,
enumMap,
tryParse,
cxxOnly,
nullable,
translateTypeAnnotation,
parser,
);
});
},
)
.filter(Boolean),
};

return typeAliasResolution(
return translateObjectTypeAnnotation(
hasteModuleName,
nullable,
typeAnnotation.members,
typeResolutionStatus,
objectTypeAnnotation,
[],
types,
aliasMap,
nullable,
enumMap,
tryParse,
cxxOnly,
parser,
);
}
case 'TSEnumDeclaration': {
Expand Down

0 comments on commit 9b24359

Please sign in to comment.