Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix codegen output for object with indexer #35344

Closed
wants to merge 13 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -225,18 +225,7 @@ describe('isObjectProperty', () => {
expect(result).toEqual(true);
});

it("returns 'true' if 'property.type' is 'ObjectTypeIndexer'", () => {
const result = isObjectProperty(
{
type: 'ObjectTypeIndexer',
...propertyStub,
},
language,
);
expect(result).toEqual(true);
});

it("returns 'false' if 'property.type' is not 'ObjectTypeProperty' or 'ObjectTypeIndexer'", () => {
it("returns 'false' if 'property.type' is not 'ObjectTypeProperty'", () => {
const result = isObjectProperty(
{
type: 'notObjectTypeProperty',
Expand All @@ -261,18 +250,7 @@ describe('isObjectProperty', () => {
expect(result).toEqual(true);
});

it("returns 'true' if 'property.type' is 'TSIndexSignature'", () => {
const result = isObjectProperty(
{
type: 'TSIndexSignature',
...propertyStub,
},
language,
);
expect(result).toEqual(true);
});

it("returns 'false' if 'property.type' is not 'TSPropertySignature' or 'TSIndexSignature'", () => {
it("returns 'false' if 'property.type' is not 'TSPropertySignature'", () => {
const result = isObjectProperty(
{
type: 'notTSPropertySignature',
Expand All @@ -295,7 +273,7 @@ describe('parseObjectProperty', () => {

describe("when 'language' is 'Flow'", () => {
const language: ParserType = 'Flow';
it("throws an 'UnsupportedObjectPropertyTypeAnnotationParserError' error if 'property.type' is not 'ObjectTypeProperty' or 'ObjectTypeIndexer'.", () => {
it("throws an 'UnsupportedObjectPropertyTypeAnnotationParserError' error if 'property.type' is not 'ObjectTypeProperty'.", () => {
const property = {
type: 'notObjectTypeProperty',
typeAnnotation: {
Expand Down Expand Up @@ -329,7 +307,7 @@ describe('parseObjectProperty', () => {

describe("when 'language' is 'TypeScript'", () => {
const language: ParserType = 'TypeScript';
it("throws an 'UnsupportedObjectPropertyTypeAnnotationParserError' error if 'property.type' is not 'TSPropertySignature' or 'TSIndexSignature'.", () => {
it("throws an 'UnsupportedObjectPropertyTypeAnnotationParserError' error if 'property.type' is not 'TSPropertySignature'.", () => {
const property = {
type: 'notTSPropertySignature',
typeAnnotation: {
Expand Down Expand Up @@ -358,40 +336,5 @@ describe('parseObjectProperty', () => {
),
).toThrow(expected);
});

it("returns a 'NativeModuleBaseTypeAnnotation' object with 'typeAnnotation.type' equal to 'GenericObjectTypeAnnotation', if 'property.type' is 'TSIndexSignature'.", () => {
const property = {
type: 'TSIndexSignature',
typeAnnotation: {
type: 'TSIndexSignature',
typeAnnotation: 'TSIndexSignature',
},
key: {
name: 'testKeyName',
},
value: 'wrongValue',
name: 'wrongName',
parameters: [{name: 'testName'}],
};
const result = parseObjectProperty(
property,
moduleName,
types,
aliasMap,
tryParse,
cxxOnly,
nullable,
typeScriptTranslateTypeAnnotation,
typeScriptParser,
);
const expected = {
name: 'testName',
optional: false,
typeAnnotation: wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
}),
};
expect(result).toEqual(expected);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,32 +37,6 @@ describe('FlowParser', () => {
});
});

describe('when propertyOrIndex is ObjectTypeIndexer', () => {
it('returns indexer name', () => {
const indexer = {
type: 'ObjectTypeIndexer',
id: {
name: 'indexerName',
},
};

const expected = 'indexerName';

expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected);
});

it('returns `key` if indexer has no name', () => {
const indexer = {
type: 'ObjectTypeIndexer',
id: {},
};

const expected = 'key';

expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected);
});
});

describe('when propertyOrIndex is not ObjectTypeProperty or ObjectTypeIndexer', () => {
it('throw UnsupportedObjectPropertyTypeAnnotationParserError', () => {
const indexer = {
Expand Down Expand Up @@ -113,23 +87,6 @@ describe('TypeScriptParser', () => {
});
});

describe('when propertyOrIndex is TSIndexSignature', () => {
it('returns indexer name', () => {
const indexer = {
type: 'TSIndexSignature',
parameters: [
{
name: 'indexerName',
},
],
};

const expected = 'indexerName';

expect(parser.getKeyName(indexer, hasteModuleName)).toEqual(expected);
});
});

describe('when propertyOrIndex is not TSPropertySignature or TSIndexSignature', () => {
it('throw UnsupportedObjectPropertyTypeAnnotationParserError', () => {
const indexer = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,7 +712,8 @@ export interface Spec extends TurboModule {
returnObjectArray(): Promise<Array<Object>>;
returnNullableNumber(): Promise<number | null>;
returnEmpty(): Promise<empty>;
returnIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnUnsupportedIndex(): Promise<{ [string]: 'authorized' | 'denied' | 'undetermined' | true | false }>;
returnSupportedIndex(): Promise<{ [string]: CustomObject }>;
returnEnum() : Promise<Season>;
returnObject() : Promise<CustomObject>;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,32 +126,14 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'b',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
'type': 'GenericObjectTypeAnnotation'
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'a',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
'type': 'GenericObjectTypeAnnotation'
}
}
]
Expand All @@ -163,32 +145,14 @@ exports[`RN Codegen Flow Parser can generate fixture CXX_ONLY_NATIVE_MODULE 1`]
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
'type': 'GenericObjectTypeAnnotation'
},
'params': [
{
'name': 'arg',
'optional': false,
'typeAnnotation': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
'type': 'GenericObjectTypeAnnotation'
}
}
]
Expand Down Expand Up @@ -1810,23 +1774,25 @@ exports[`RN Codegen Flow Parser can generate fixture PROMISE_WITH_COMMONLY_USED_
}
},
{
'name': 'returnIndex',
'name': 'returnUnsupportedIndex',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'PromiseTypeAnnotation'
},
'params': []
}
},
{
'name': 'returnSupportedIndex',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'PromiseTypeAnnotation',
'elementType': {
'type': 'ObjectTypeAnnotation',
'properties': [
{
'name': 'key',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
'type': 'GenericObjectTypeAnnotation'
}
},
'params': []
Expand Down
25 changes: 24 additions & 1 deletion packages/react-native-codegen/src/parsers/flow/modules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,28 @@ function translateTypeAnnotation(
}
}
case 'ObjectTypeAnnotation': {
// if there is any indexer, then it is a dictionary
if (typeAnnotation.indexers) {
const indexers = typeAnnotation.indexers.filter(
member => member.type === 'ObjectTypeIndexer',
);
if (indexers.length > 0) {
// check the property type to prevent developers from using unsupported types
// the return value from `translateTypeAnnotation` is unused
const propertyType = indexers[0].value;
translateTypeAnnotation(
ZihanChen-MSFT marked this conversation as resolved.
Show resolved Hide resolved
hasteModuleName,
propertyType,
types,
aliasMap,
tryParse,
cxxOnly,
);
// no need to do further checking
return emitObject(nullable);
}
}

const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
// $FlowFixMe[missing-type-arg]
Expand Down Expand Up @@ -238,8 +260,9 @@ function translateTypeAnnotation(
case 'MixedTypeAnnotation': {
if (cxxOnly) {
return emitMixed(nullable);
} else {
return emitObject(nullable);
}
// Fallthrough
}
default: {
throw new UnsupportedTypeAnnotationParserError(
Expand Down
27 changes: 13 additions & 14 deletions packages/react-native-codegen/src/parsers/flow/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,20 @@ const {
class FlowParser implements Parser {
typeParameterInstantiation: string = 'TypeParameterInstantiation';

getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string {
switch (propertyOrIndex.type) {
case 'ObjectTypeProperty':
return propertyOrIndex.key.name;
case 'ObjectTypeIndexer':
ZihanChen-MSFT marked this conversation as resolved.
Show resolved Hide resolved
// flow index name is optional
return propertyOrIndex.id?.name ?? 'key';
default:
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
propertyOrIndex,
propertyOrIndex.type,
this.language(),
);
isProperty(property: $FlowFixMe): boolean {
return property.type === 'ObjectTypeProperty';
}

getKeyName(property: $FlowFixMe, hasteModuleName: string): string {
if (!this.isProperty(property)) {
throw new UnsupportedObjectPropertyTypeAnnotationParserError(
hasteModuleName,
property,
property.type,
this.language(),
);
}
return property.key.name;
}

getMaybeEnumMemberType(maybeEnumDeclaration: $FlowFixMe): string {
Expand Down
12 changes: 8 additions & 4 deletions packages/react-native-codegen/src/parsers/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ export interface Parser {
typeParameterInstantiation: string;

/**
* Given a property or an index declaration, it returns the key name.
* @parameter propertyOrIndex: an object containing a property or an index declaration.
* Given a declaration, it returns true if it is a property
*/
isProperty(property: $FlowFixMe): boolean;
/**
* Given a property declaration, it returns the key name.
* @parameter property: an object containing a property declaration.
* @parameter hasteModuleName: a string with the native module name.
* @returns: the key name.
* @throws if propertyOrIndex does not contain a property or an index declaration.
* @throws if property does not contain a property declaration.
*/
getKeyName(propertyOrIndex: $FlowFixMe, hasteModuleName: string): string;
getKeyName(property: $FlowFixMe, hasteModuleName: string): string;
/**
* Given a type declaration, it possibly returns the name of the Enum type.
* @parameter maybeEnumDeclaration: an object possibly containing an Enum declaration.
Expand Down
Loading