Refactor translate UniontypeAnnotation (#35050)

Summary:
Part of https://github.com/facebook/react-native/issues/34872
> [Assigned to youedd] This task is more advanced than the others Create a function to unify the [unionType](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/flow/modules/index.js#L372-L396) and the [TSUnionType](https://github.com/facebook/react-native/blob/main/packages/react-native-codegen/src/parsers/typescript/modules/index.js#L407-L431). This task may share some logic from the previous task. The function should accept a ParserType parameter to implement the proper logic to extract the memberType variable. Ideally, we should create a couple of supporting functions to implement those logics.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
-->

[Internal] [Changed] - Refactor translate UniontypeAnnotation

Pull Request resolved: https://github.com/facebook/react-native/pull/35050

Test Plan:
`yarn jest react-native-codegen`
![image](https://user-images.githubusercontent.com/19575877/197334084-2daeed7e-8e87-4140-b8c3-07f2de3f0496.png)

Reviewed By: cortinico

Differential Revision: D40637299

Pulled By: cipolleschi

fbshipit-source-id: 1490001a3398c3af79d465c40de3c3687f058523
This commit is contained in:
youedd
2022-10-26 10:02:01 -07:00
committed by Facebook GitHub Bot
parent cba56eadd2
commit 0627cd69cd
5 changed files with 509 additions and 50 deletions
@@ -12,12 +12,15 @@
'use-strict';
import {assertGenericTypeAnnotationHasExactlyOneTypeParameter} from '../parsers-commons';
import type {ParserType} from '../errors';
const {
wrapNullable,
unwrapNullable,
emitMixedTypeAnnotation,
emitUnionTypeAnnotation,
} = require('../parsers-commons.js');
const {UnsupportedUnionTypeAnnotationParserError} = require('../errors');
import type {UnionTypeAnnotationMemberType} from '../../CodegenSchema';
describe('wrapNullable', () => {
describe('when nullable is true', () => {
@@ -230,3 +233,434 @@ describe('emitMixedTypeAnnotation', () => {
});
});
});
describe('emitUnionTypeAnnotation', () => {
const hasteModuleName = 'SampleTurboModule';
describe('when language is flow', () => {
const language: ParserType = 'Flow';
describe('when members type is numeric', () => {
const typeAnnotation = {
type: 'UnionTypeAnnotation',
types: [
{type: 'NumberLiteralTypeAnnotation'},
{type: 'NumberLiteralTypeAnnotation'},
],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'NumberTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'NumberTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is string', () => {
const typeAnnotation = {
type: 'UnionTypeAnnotation',
types: [
{type: 'StringLiteralTypeAnnotation'},
{type: 'StringLiteralTypeAnnotation'},
],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is object', () => {
const typeAnnotation = {
type: 'UnionTypeAnnotation',
types: [{type: 'ObjectTypeAnnotation'}, {type: 'ObjectTypeAnnotation'}],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'ObjectTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'ObjectTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is mixed', () => {
const typeAnnotation = {
type: 'UnionTypeAnnotation',
types: [
{type: 'NumberLiteralTypeAnnotation'},
{type: 'StringLiteralTypeAnnotation'},
{type: 'ObjectTypeAnnotation'},
],
};
const unionTypes: UnionTypeAnnotationMemberType[] = [
'NumberTypeAnnotation',
'StringTypeAnnotation',
'ObjectTypeAnnotation',
];
describe('when nullable is true', () => {
it('throws an excpetion', () => {
const expected = new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
expect(() => {
emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
}).toThrow(expected);
});
});
describe('when nullable is false', () => {
it('throws an excpetion', () => {
const expected = new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
expect(() => {
emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
}).toThrow(expected);
});
});
});
});
describe('when language is typescript', () => {
const language: ParserType = 'TypeScript';
describe('when members type is numeric', () => {
const typeAnnotation = {
type: 'TSUnionType',
types: [
{
type: 'TSLiteralType',
literal: {type: 'NumericLiteral'},
},
{
type: 'TSLiteralType',
literal: {type: 'NumericLiteral'},
},
],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'NumberTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'NumberTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is string', () => {
const typeAnnotation = {
type: 'TSUnionType',
types: [
{
type: 'TSLiteralType',
literal: {type: 'StringLiteral'},
},
{
type: 'TSLiteralType',
literal: {type: 'StringLiteral'},
},
],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is object', () => {
const typeAnnotation = {
type: 'TSUnionType',
types: [
{
type: 'TSLiteralType',
},
{
type: 'TSLiteralType',
},
],
};
describe('when nullable is true', () => {
it('returns nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'NullableTypeAnnotation',
typeAnnotation: {
type: 'UnionTypeAnnotation',
memberType: 'ObjectTypeAnnotation',
},
};
expect(result).toEqual(expected);
});
});
describe('when nullable is false', () => {
it('returns non nullable type annotation', () => {
const result = emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
const expected = {
type: 'UnionTypeAnnotation',
memberType: 'ObjectTypeAnnotation',
};
expect(result).toEqual(expected);
});
});
});
describe('when members type is mixed', () => {
const typeAnnotation = {
type: 'TSUnionType',
types: [
{
type: 'TSLiteralType',
literal: {type: 'NumericLiteral'},
},
{
type: 'TSLiteralType',
literal: {type: 'StringLiteral'},
},
{
type: 'TSLiteralType',
},
],
};
const unionTypes = [
'NumberTypeAnnotation',
'StringTypeAnnotation',
'ObjectTypeAnnotation',
];
describe('when nullable is true', () => {
it('throws an excpetion', () => {
const expected = new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
expect(() => {
emitUnionTypeAnnotation(
true,
hasteModuleName,
typeAnnotation,
language,
);
}).toThrow(expected);
});
});
describe('when nullable is false', () => {
it('throws an excpetion', () => {
const expected = new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
expect(() => {
emitUnionTypeAnnotation(
false,
hasteModuleName,
typeAnnotation,
language,
);
}).toThrow(expected);
});
});
});
});
});
+3 -1
View File
@@ -10,6 +10,8 @@
'use strict';
import type {UnionTypeAnnotationMemberType} from '../CodegenSchema';
const invariant = require('invariant');
import type {Parser} from './parser';
export type ParserType = 'Flow' | 'TypeScript';
@@ -308,7 +310,7 @@ class UnsupportedUnionTypeAnnotationParserError extends ParserError {
constructor(
nativeModuleName: string,
arrayElementTypeAST: $FlowFixMe,
types: string[],
types: UnionTypeAnnotationMemberType[],
language: ParserType,
) {
super(
@@ -38,6 +38,7 @@ const {
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
emitMixedTypeAnnotation,
emitUnionTypeAnnotation,
} = require('../../parsers-commons');
const {
emitBoolean,
@@ -375,29 +376,12 @@ function translateTypeAnnotation(
}
case 'UnionTypeAnnotation': {
if (cxxOnly) {
// Remap literal names
const unionTypes = typeAnnotation.types
.map(
item =>
item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation'),
// ObjectAnnotation is already 'correct'
)
.filter((value, index, self) => self.indexOf(value) === index);
// Only support unionTypes of the same kind
if (unionTypes.length > 1) {
throw new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
}
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: unionTypes[0],
});
return emitUnionTypeAnnotation(
nullable,
hasteModuleName,
typeAnnotation,
language,
);
}
// Fallthrough
}
@@ -16,8 +16,13 @@ import type {
NativeModuleTypeAnnotation,
Nullable,
NativeModuleMixedTypeAnnotation,
UnionTypeAnnotationMemberType,
NativeModuleUnionTypeAnnotation,
} from '../CodegenSchema.js';
const {IncorrectlyParameterizedGenericParserError} = require('./errors');
const {
IncorrectlyParameterizedGenericParserError,
UnsupportedUnionTypeAnnotationParserError,
} = require('./errors');
import type {ParserType} from './errors';
const invariant = require('invariant');
@@ -100,10 +105,61 @@ function emitMixedTypeAnnotation(
});
}
function remapUnionTypeAnnotationMemberNames(
types: $FlowFixMe,
language: ParserType,
): UnionTypeAnnotationMemberType[] {
const remapLiteral = (item: $FlowFixMe) => {
if (language === 'Flow') {
return item.type
.replace('NumberLiteralTypeAnnotation', 'NumberTypeAnnotation')
.replace('StringLiteralTypeAnnotation', 'StringTypeAnnotation');
}
return item.literal
? item.literal.type
.replace('NumericLiteral', 'NumberTypeAnnotation')
.replace('StringLiteral', 'StringTypeAnnotation')
: 'ObjectTypeAnnotation';
};
return types
.map(remapLiteral)
.filter((value, index, self) => self.indexOf(value) === index);
}
function emitUnionTypeAnnotation(
nullable: boolean,
hasteModuleName: string,
typeAnnotation: $FlowFixMe,
language: ParserType,
): Nullable<NativeModuleUnionTypeAnnotation> {
const unionTypes = remapUnionTypeAnnotationMemberNames(
typeAnnotation.types,
language,
);
// Only support unionTypes of the same kind
if (unionTypes.length > 1) {
throw new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
}
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: unionTypes[0],
});
}
module.exports = {
wrapModuleSchema,
unwrapNullable,
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
emitMixedTypeAnnotation,
emitUnionTypeAnnotation,
};
@@ -38,6 +38,7 @@ const {
wrapNullable,
assertGenericTypeAnnotationHasExactlyOneTypeParameter,
emitMixedTypeAnnotation,
emitUnionTypeAnnotation,
} = require('../../parsers-commons');
const {
emitBoolean,
@@ -60,7 +61,6 @@ const {
UnsupportedTypeAnnotationParserError,
UnsupportedFunctionParamTypeAnnotationParserError,
UnsupportedEnumDeclarationParserError,
UnsupportedUnionTypeAnnotationParserError,
UnsupportedObjectPropertyTypeAnnotationParserError,
IncorrectModuleRegistryCallArgumentTypeParserError,
} = require('../../errors.js');
@@ -390,29 +390,12 @@ function translateTypeAnnotation(
}
case 'TSUnionType': {
if (cxxOnly) {
// Remap literal names
const unionTypes = typeAnnotation.types
.map(item =>
item.literal
? item.literal.type
.replace('NumericLiteral', 'NumberTypeAnnotation')
.replace('StringLiteral', 'StringTypeAnnotation')
: 'ObjectTypeAnnotation',
)
.filter((value, index, self) => self.indexOf(value) === index);
// Only support unionTypes of the same kind
if (unionTypes.length > 1) {
throw new UnsupportedUnionTypeAnnotationParserError(
hasteModuleName,
typeAnnotation,
unionTypes,
language,
);
}
return wrapNullable(nullable, {
type: 'UnionTypeAnnotation',
memberType: unionTypes[0],
});
return emitUnionTypeAnnotation(
nullable,
hasteModuleName,
typeAnnotation,
language,
);
}
// Fallthrough
}