diff --git a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js index 51184fdfb85..48ceb29419b 100644 --- a/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js +++ b/packages/react-native-codegen/src/parsers/__tests__/error-utils-test.js @@ -25,6 +25,7 @@ const { throwIfUntypedModule, throwIfUnsupportedFunctionParamTypeAnnotationParserError, throwIfArrayElementTypeAnnotationIsUnsupported, + throwIfPartialNotAnnotatingTypeParameter, } = require('../error-utils'); const { UnsupportedModulePropertyParserError, @@ -720,3 +721,78 @@ describe('throwIfArrayElementTypeAnnotationIsUnsupported', () => { }).not.toThrow(UnsupportedArrayElementTypeAnnotationParserError); }); }); + +describe('throwIfPartialNotAnnotatingTypeParameter', () => { + const flowParser = new FlowParser(); + const typescriptParser = new TypeScriptParser(); + const typerscriptTypeAnnotation = { + typeParameters: { + params: [ + { + typeName: { + name: 'TypeDeclaration', + }, + }, + ], + }, + }; + const flowTypeAnnotation = { + typeParameters: { + params: [ + { + id: { + name: 'TypeDeclaration', + }, + }, + ], + }, + }; + + it('throw error if Partial Not Annotating Type Parameter in Flow', () => { + const types = {}; + + expect(() => { + throwIfPartialNotAnnotatingTypeParameter( + flowTypeAnnotation, + types, + flowParser, + ); + }).toThrowError('Partials only support annotating a type parameter.'); + }); + + it('throw error if Partial Not Annotating Type Parameter in TypeScript', () => { + const types = {}; + + expect(() => { + throwIfPartialNotAnnotatingTypeParameter( + typerscriptTypeAnnotation, + types, + typescriptParser, + ); + }).toThrowError('Partials only support annotating a type parameter.'); + }); + + it('does not throw error if Partial Annotating Type Parameter in Flow', () => { + const types = {TypeDeclaration: {}}; + + expect(() => { + throwIfPartialNotAnnotatingTypeParameter( + flowTypeAnnotation, + types, + flowParser, + ); + }).not.toThrowError(); + }); + + it('does not throw error if Partial Annotating Type Parameter in TypeScript', () => { + const types = {TypeDeclaration: {}}; + + expect(() => { + throwIfPartialNotAnnotatingTypeParameter( + typerscriptTypeAnnotation, + types, + typescriptParser, + ); + }).not.toThrowError(); + }); +}); diff --git a/packages/react-native-codegen/src/parsers/error-utils.js b/packages/react-native-codegen/src/parsers/error-utils.js index fea578eecb8..5bddcf4d3f6 100644 --- a/packages/react-native-codegen/src/parsers/error-utils.js +++ b/packages/react-native-codegen/src/parsers/error-utils.js @@ -13,6 +13,7 @@ import type {NativeModuleTypeAnnotation} from '../CodegenSchema'; import type {ParserType} from './errors'; import type {Parser} from './parser'; +import type {TypeDeclarationMap} from '../parsers/utils'; const { MisnamedModuleInterfaceParserError, @@ -280,6 +281,21 @@ function throwIfIncorrectModuleRegistryCallArgument( } } +function throwIfPartialNotAnnotatingTypeParameter( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + parser: Parser, +) { + const annotatedElement = parser.extractAnnotatedElement( + typeAnnotation, + types, + ); + + if (!annotatedElement) { + throw new Error('Partials only support annotating a type parameter.'); + } +} + module.exports = { throwIfModuleInterfaceIsMisnamed, throwIfUnsupportedFunctionReturnTypeAnnotationParserError, @@ -295,4 +311,5 @@ module.exports = { throwIfUnsupportedFunctionParamTypeAnnotationParserError, throwIfArrayElementTypeAnnotationIsUnsupported, throwIfIncorrectModuleRegistryCallArgument, + throwIfPartialNotAnnotatingTypeParameter, }; 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 9b649f98bd4..03dff7ef80a 100644 --- a/packages/react-native-codegen/src/parsers/flow/modules/index.js +++ b/packages/react-native-codegen/src/parsers/flow/modules/index.js @@ -69,6 +69,7 @@ const { throwIfIncorrectModuleRegistryCallArgument, throwIfUntypedModule, throwIfMoreThanOneModuleInterfaceParserError, + throwIfPartialNotAnnotatingTypeParameter, } = require('../../error-utils'); const language = 'Flow'; @@ -168,14 +169,16 @@ function translateTypeAnnotation( ); } - const annotatedElement = - types[typeAnnotation.typeParameters.params[0].id.name]; + const annotatedElement = parser.extractAnnotatedElement( + typeAnnotation, + types, + ); - if (!annotatedElement) { - throw new Error( - 'Partials only support annotating a type parameter.', - ); - } + throwIfPartialNotAnnotatingTypeParameter( + typeAnnotation, + types, + parser, + ); const properties = annotatedElement.right.properties.map(prop => { return { diff --git a/packages/react-native-codegen/src/parsers/flow/parser.js b/packages/react-native-codegen/src/parsers/flow/parser.js index 4eafbdebcb8..4d6e84836a3 100644 --- a/packages/react-native-codegen/src/parsers/flow/parser.js +++ b/packages/react-native-codegen/src/parsers/flow/parser.js @@ -21,6 +21,7 @@ import type { } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +import type {TypeDeclarationMap} from '../utils'; // $FlowFixMe[untyped-import] there's no flowtype flow-parser const flowParser = require('flow-parser'); @@ -205,6 +206,13 @@ class FlowParser implements Parser { node.extends[0].id.name === 'TurboModule' ); } + + extractAnnotatedElement( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): $FlowFixMe { + return types[typeAnnotation.typeParameters.params[0].id.name]; + } } module.exports = { diff --git a/packages/react-native-codegen/src/parsers/parser.js b/packages/react-native-codegen/src/parsers/parser.js index 5520deccd73..54e9605dafd 100644 --- a/packages/react-native-codegen/src/parsers/parser.js +++ b/packages/react-native-codegen/src/parsers/parser.js @@ -20,6 +20,7 @@ import type { NativeModuleEnumMembers, } from '../CodegenSchema'; import type {ParserType} from './errors'; +import type {TypeDeclarationMap} from './utils'; /** * This is the main interface for Parsers of various languages. @@ -157,4 +158,15 @@ export interface Parser { * Given a node, it returns true if it is a module interface */ isModuleInterface(node: $FlowFixMe): boolean; + + /** + * Given a typeAnnotation, it returns the annotated element. + * @paramater typeAnnotation: the annotation for a type. + * @paramater types: a map of type declarations. + * @returns: the annotated element. + */ + extractAnnotatedElement( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): $FlowFixMe; } diff --git a/packages/react-native-codegen/src/parsers/parserMock.js b/packages/react-native-codegen/src/parsers/parserMock.js index 39e45ee1f2e..75874a0b85e 100644 --- a/packages/react-native-codegen/src/parsers/parserMock.js +++ b/packages/react-native-codegen/src/parsers/parserMock.js @@ -22,6 +22,8 @@ import type { NativeModuleEnumMembers, } from '../CodegenSchema'; +import type {TypeDeclarationMap} from './utils'; + // $FlowFixMe[untyped-import] there's no flowtype flow-parser const flowParser = require('flow-parser'); const { @@ -168,4 +170,11 @@ export class MockedParser implements Parser { node.extends[0].id.name === 'TurboModule' ); } + + extractAnnotatedElement( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): $FlowFixMe { + return types[typeAnnotation.typeParameters.params[0].id.name]; + } } 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 5b0ff7c7ef2..ab661e0ef15 100644 --- a/packages/react-native-codegen/src/parsers/typescript/modules/index.js +++ b/packages/react-native-codegen/src/parsers/typescript/modules/index.js @@ -75,6 +75,7 @@ const { throwIfMoreThanOneModuleInterfaceParserError, throwIfIncorrectModuleRegistryCallTypeParameterParserError, throwIfIncorrectModuleRegistryCallArgument, + throwIfPartialNotAnnotatingTypeParameter, } = require('../../error-utils'); const language = 'TypeScript'; @@ -249,14 +250,16 @@ function translateTypeAnnotation( ); } - const annotatedElement = - types[typeAnnotation.typeParameters.params[0].typeName.name]; + const annotatedElement = parser.extractAnnotatedElement( + typeAnnotation, + types, + ); - if (!annotatedElement) { - throw new Error( - 'Partials only support annotating a type parameter.', - ); - } + throwIfPartialNotAnnotatingTypeParameter( + typeAnnotation, + types, + parser, + ); const properties = annotatedElement.typeAnnotation.members.map( member => { diff --git a/packages/react-native-codegen/src/parsers/typescript/parser.js b/packages/react-native-codegen/src/parsers/typescript/parser.js index b200de3bbe5..b050c9a87ac 100644 --- a/packages/react-native-codegen/src/parsers/typescript/parser.js +++ b/packages/react-native-codegen/src/parsers/typescript/parser.js @@ -21,6 +21,7 @@ import type { } from '../../CodegenSchema'; import type {ParserType} from '../errors'; import type {Parser} from '../parser'; +import type {TypeDeclarationMap} from '../utils'; // $FlowFixMe[untyped-import] Use flow-types for @babel/parser const babelParser = require('@babel/parser'); @@ -201,6 +202,13 @@ class TypeScriptParser implements Parser { node.extends[0].expression.name === 'TurboModule' ); } + + extractAnnotatedElement( + typeAnnotation: $FlowFixMe, + types: TypeDeclarationMap, + ): $FlowFixMe { + return types[typeAnnotation.typeParameters.params[0].typeName.name]; + } } module.exports = { TypeScriptParser,