Introduce NullableTypeAnnotation for Flow Module Parser

Summary:
Previously, all our type annotations contained a `nullable` property. This diff removes that property from all our NativeModule type annotations, and instead introduces a `NullableTypeAnnotation`.

**Some Benefits:**
- In all our serialization functions, we use Flow exhaustive checking to ensure that all type-annotations can be serialized. Since nullability is now recorded as a type annotation, Flow will ensure we always explicitly handle nullability. Previously, with nullability as a property, we could ignore it without any feedback from flow.
- This aligns the NativeModule schema with the ESTree spec.
- After this diff, we're one step closer to sharing type annotations with Codegen's schema. Many NativeModule type annotations now have the same shape as their Codegen counterparts. They will be merged in a subsequent diff.

**Downsides:**
- If you want to check whether a type annotation is of type `T`, you have to remember to unwrap the type annotation *yourself*. Flow won't warn you if you forget to unwrap the type, which can lead to incomplete handling to nullable types in our generators.
- When you're creating type annotations in code, previously, you *had* to specify nullability, since it was a property on all type annotation objects. Now, it's very possible for you to forget to wrap the type annotation, which will just lead to nullability bugs.

**Notes:**
- In the scheam, exported type annotations are *always* required. They can be made nullable using the new `Nullable` genric type.

Changelog: [Internal]

Reviewed By: fkgozali

Differential Revision: D24026887

fbshipit-source-id: 9e71e2c6102dc506824403dbb712488ca8507d08
This commit is contained in:
Ramanpreet Nara
2020-10-01 19:30:07 -07:00
committed by Facebook GitHub Bot
parent 0a2e0f777b
commit 8d807dfbc3
5 changed files with 469 additions and 456 deletions
+18 -27
View File
@@ -258,13 +258,7 @@ export type SchemaType = $ReadOnly<{|
/**
* NativeModule Types
*
* TODO(T71923114): Remove nullable from NativeModule type annotations.
* This is necessary for us to start unifying the notion of a "type
* annotation" across the codegen schema as a whole.
*/
export type Required<T> = $ReadOnly<{...T, nullable: false}>;
export type NativeModuleSchema = $ReadOnly<{|
// We only support aliases to Objects
aliases: NativeModuleAliasMap,
@@ -272,26 +266,36 @@ export type NativeModuleSchema = $ReadOnly<{|
|}>;
export type NativeModuleAliasMap = $ReadOnly<{|
[aliasName: string]: Required<NativeModuleObjectTypeAnnotation>,
[aliasName: string]: NativeModuleObjectTypeAnnotation,
|}>;
export type NativeModulePropertySchema = $ReadOnly<{|
name: string,
optional: boolean,
typeAnnotation: NativeModuleFunctionTypeAnnotation,
typeAnnotation: Nullable<NativeModuleFunctionTypeAnnotation>,
|}>;
export type Nullable<+T: NativeModuleTypeAnnotation> =
| NullableTypeAnnotation<T>
| T;
export type NullableTypeAnnotation<
+T: NativeModuleTypeAnnotation,
> = $ReadOnly<{|
type: 'NullableTypeAnnotation',
typeAnnotation: T,
|}>;
export type NativeModuleFunctionTypeAnnotation = $ReadOnly<{|
type: 'FunctionTypeAnnotation',
params: $ReadOnlyArray<NativeModuleMethodParamSchema>,
returnTypeAnnotation: NativeModuleReturnTypeAnnotation,
nullable: boolean,
returnTypeAnnotation: Nullable<NativeModuleReturnTypeAnnotation>,
|}>;
export type NativeModuleMethodParamSchema = $ReadOnly<{|
name: string,
optional: boolean,
typeAnnotation: NativeModuleParamTypeAnnotation,
typeAnnotation: Nullable<NativeModuleParamTypeAnnotation>,
|}>;
export type NativeModuleObjectTypeAnnotation = $ReadOnly<{|
@@ -300,14 +304,13 @@ export type NativeModuleObjectTypeAnnotation = $ReadOnly<{|
$ReadOnly<{|
name: string,
optional: boolean,
typeAnnotation: NativeModuleBaseTypeAnnotation,
typeAnnotation: Nullable<NativeModuleBaseTypeAnnotation>,
|}>,
>,
nullable: boolean,
|}>;
export type NativeModuleArrayTypeAnnotation<
T = NativeModuleBaseTypeAnnotation,
+T: Nullable<NativeModuleBaseTypeAnnotation>,
> = $ReadOnly<{|
type: 'ArrayTypeAnnotation',
/**
@@ -315,64 +318,52 @@ export type NativeModuleArrayTypeAnnotation<
* invalid Array ElementTypes. Then, make the elementType required.
*/
elementType?: T,
nullable: boolean,
|}>;
export type NativeModuleStringTypeAnnotation = $ReadOnly<{|
type: 'StringTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleNumberTypeAnnotation = $ReadOnly<{|
type: 'NumberTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleInt32TypeAnnotation = $ReadOnly<{|
type: 'Int32TypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleDoubleTypeAnnotation = $ReadOnly<{|
type: 'DoubleTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleFloatTypeAnnotation = $ReadOnly<{|
type: 'FloatTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleBooleanTypeAnnotation = $ReadOnly<{|
type: 'BooleanTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleGenericObjectTypeAnnotation = $ReadOnly<{|
type: 'GenericObjectTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleReservedFunctionValueTypeAnnotation = $ReadOnly<{|
type: 'ReservedFunctionValueTypeAnnotation',
name: ReservedFunctionValueTypeName,
nullable: boolean,
|}>;
export type NativeModuleTypeAliasTypeAnnotation = $ReadOnly<{|
type: 'TypeAliasTypeAnnotation',
name: string,
nullable: boolean,
|}>;
export type NativeModulePromiseTypeAnnotation = $ReadOnly<{|
type: 'PromiseTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleVoidTypeAnnotation = $ReadOnly<{|
type: 'VoidTypeAnnotation',
nullable: boolean,
|}>;
export type NativeModuleBaseTypeAnnotation =
@@ -385,7 +376,7 @@ export type NativeModuleBaseTypeAnnotation =
| NativeModuleGenericObjectTypeAnnotation
| NativeModuleReservedFunctionValueTypeAnnotation
| NativeModuleTypeAliasTypeAnnotation
| NativeModuleArrayTypeAnnotation<>
| NativeModuleArrayTypeAnnotation<Nullable<NativeModuleBaseTypeAnnotation>>
| NativeModuleObjectTypeAnnotation;
export type NativeModuleParamTypeAnnotation =
@@ -34,28 +34,27 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
SampleTurboModule: {
aliases: {
ObjectAlias: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'x',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
},
{
name: 'y',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
},
{
name: 'label',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
},
{
name: 'truthy',
optional: false,
typeAnnotation: { nullable: false, type: 'BooleanTypeAnnotation' }
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -66,15 +65,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
],
nullable: false
]
}
},
{
@@ -82,9 +80,8 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
params: [],
nullable: false
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: []
}
},
{
@@ -93,13 +90,12 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'a',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
]
},
@@ -108,13 +104,11 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
name: 'a',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'NumberTypeAnnotation' }
elementType: { type: 'NumberTypeAnnotation' }
}
}
],
nullable: false
]
}
},
{
@@ -122,19 +116,17 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ALIASES
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'StringTypeAnnotation' },
returnTypeAnnotation: { type: 'StringTypeAnnotation' },
params: [
{
name: 'a',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'ObjectAlias'
}
}
],
nullable: false
]
}
}
]
@@ -159,22 +151,19 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WI
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
},
params: [
{
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
}
}
],
nullable: false
]
}
}
]
@@ -198,15 +187,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ARRAY_WI
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'ArrayTypeAnnotation' },
returnTypeAnnotation: { type: 'ArrayTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'ArrayTypeAnnotation' }
typeAnnotation: { type: 'ArrayTypeAnnotation' }
}
],
nullable: false
]
}
}
]
@@ -231,22 +219,19 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_AR
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
},
params: [
{
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
}
}
],
nullable: false
]
}
},
{
@@ -255,22 +240,19 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_AR
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
},
params: [
{
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
}
}
],
nullable: false
]
}
}
]
@@ -294,15 +276,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA
optional: true,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'BooleanTypeAnnotation' }
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
],
nullable: false
]
}
},
{
@@ -310,15 +291,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
],
nullable: false
]
}
},
{
@@ -326,15 +306,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
}
],
nullable: false
]
}
},
{
@@ -342,15 +321,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_BASIC_PA
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
}
],
nullable: false
]
}
}
]
@@ -374,45 +352,35 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_CALLBACK
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'callback',
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'value',
optional: false,
typeAnnotation: {
nullable: false,
type: 'StringTypeAnnotation'
}
typeAnnotation: { type: 'StringTypeAnnotation' }
},
{
name: 'arr',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'StringTypeAnnotation'
}
elementType: { type: 'StringTypeAnnotation' }
}
}
}
],
nullable: false
]
}
}
],
nullable: false
]
}
}
]
@@ -437,15 +405,12 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: { nullable: false, type: 'StringTypeAnnotation' }
elementType: { type: 'StringTypeAnnotation' }
}
}
},
@@ -454,32 +419,23 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'StringTypeAnnotation'
}
elementType: { type: 'StringTypeAnnotation' }
}
}
}
}
}
}
],
nullable: false
]
}
}
]
@@ -504,23 +460,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -532,23 +483,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -556,8 +502,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
]
}
}
],
nullable: false
]
}
},
{
@@ -566,23 +511,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -594,23 +534,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -618,8 +553,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
]
}
}
],
nullable: false
]
}
},
{
@@ -627,31 +561,23 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'GenericObjectTypeAnnotation'
},
returnTypeAnnotation: { type: 'GenericObjectTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'a',
optional: false,
typeAnnotation: {
nullable: false,
type: 'StringTypeAnnotation'
}
typeAnnotation: { type: 'StringTypeAnnotation' }
}
]
}
}
],
nullable: false
]
}
},
{
@@ -660,26 +586,20 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ArrayTypeAnnotation',
elementType: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -692,23 +612,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
name: 'arg',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'const1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
}
]
}
@@ -716,8 +631,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
]
}
}
],
nullable: false
]
}
}
]
@@ -742,54 +656,40 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'isTesting',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
},
{
name: 'reactNativeVersion',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'major',
optional: false,
typeAnnotation: {
nullable: false,
type: 'NumberTypeAnnotation'
}
typeAnnotation: { type: 'NumberTypeAnnotation' }
},
{
name: 'minor',
optional: false,
typeAnnotation: {
nullable: false,
type: 'NumberTypeAnnotation'
}
typeAnnotation: { type: 'NumberTypeAnnotation' }
},
{
name: 'patch',
optional: true,
typeAnnotation: {
nullable: false,
type: 'NumberTypeAnnotation'
}
typeAnnotation: { type: 'NumberTypeAnnotation' }
},
{
name: 'prerelease',
optional: false,
typeAnnotation: {
nullable: true,
type: 'NumberTypeAnnotation'
type: 'NullableTypeAnnotation',
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
}
]
@@ -798,30 +698,26 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_COMPLEX_
{
name: 'forceTouchAvailable',
optional: false,
typeAnnotation: {
nullable: false,
type: 'BooleanTypeAnnotation'
}
typeAnnotation: { type: 'BooleanTypeAnnotation' }
},
{
name: 'osVersion',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
},
{
name: 'systemName',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
},
{
name: 'interfaceIdiom',
optional: false,
typeAnnotation: { nullable: false, type: 'StringTypeAnnotation' }
typeAnnotation: { type: 'StringTypeAnnotation' }
}
]
},
params: [],
nullable: false
params: []
}
}
]
@@ -845,15 +741,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_FLOAT_AN
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'Int32TypeAnnotation' },
returnTypeAnnotation: { type: 'Int32TypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'Int32TypeAnnotation' }
typeAnnotation: { type: 'Int32TypeAnnotation' }
}
],
nullable: false
]
}
},
{
@@ -861,15 +756,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_FLOAT_AN
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'FloatTypeAnnotation' },
returnTypeAnnotation: { type: 'FloatTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: false, type: 'FloatTypeAnnotation' }
typeAnnotation: { type: 'FloatTypeAnnotation' }
}
],
nullable: false
]
}
}
]
@@ -888,37 +782,27 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NESTED_A
SampleTurboModule: {
aliases: {
Bar: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'z',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
]
},
Foo: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'bar1',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'Bar'
}
typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Bar' }
},
{
name: 'bar2',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'Bar'
}
typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Bar' }
}
]
}
@@ -929,23 +813,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NESTED_A
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'Foo'
},
returnTypeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Foo' },
params: [
{
name: 'x',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'Foo'
}
typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Foo' }
}
],
nullable: false
]
}
},
{
@@ -953,19 +828,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NESTED_A
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'x',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'Foo'
}
typeAnnotation: { type: 'TypeAliasTypeAnnotation', name: 'Foo' }
}
],
nullable: false
]
}
}
]
@@ -989,15 +859,17 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_NULLABLE
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'VoidTypeAnnotation' },
returnTypeAnnotation: { type: 'VoidTypeAnnotation' },
params: [
{
name: 'arg',
optional: false,
typeAnnotation: { nullable: true, type: 'StringTypeAnnotation' }
typeAnnotation: {
type: 'NullableTypeAnnotation',
typeAnnotation: { type: 'StringTypeAnnotation' }
}
}
],
nullable: false
]
}
}
]
@@ -1016,13 +888,12 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
SampleTurboModule: {
aliases: {
DisplayMetricsAndroid: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'width',
optional: false,
typeAnnotation: { nullable: false, type: 'NumberTypeAnnotation' }
typeAnnotation: { type: 'NumberTypeAnnotation' }
}
]
}
@@ -1034,21 +905,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'Dimensions',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'windowPhysicalPixels',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'DisplayMetricsAndroid'
}
@@ -1058,8 +926,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
}
]
},
params: [],
nullable: false
params: []
}
},
{
@@ -1068,21 +935,18 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'Dimensions',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ObjectTypeAnnotation',
properties: [
{
name: 'windowPhysicalPixels',
optional: false,
typeAnnotation: {
nullable: false,
type: 'TypeAliasTypeAnnotation',
name: 'DisplayMetricsAndroid'
}
@@ -1092,8 +956,7 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
}
]
},
params: [],
nullable: false
params: []
}
}
]
@@ -1117,9 +980,8 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'PromiseTypeAnnotation' },
params: [],
nullable: false
returnTypeAnnotation: { type: 'PromiseTypeAnnotation' },
params: []
}
},
{
@@ -1127,9 +989,8 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'PromiseTypeAnnotation' },
params: [],
nullable: false
returnTypeAnnotation: { type: 'PromiseTypeAnnotation' },
params: []
}
},
{
@@ -1137,9 +998,8 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PROMISE
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: { nullable: false, type: 'PromiseTypeAnnotation' },
params: [],
nullable: false
returnTypeAnnotation: { type: 'PromiseTypeAnnotation' },
params: []
}
}
]
@@ -1164,7 +1024,6 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ROOT_TAG
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'ReservedFunctionValueTypeAnnotation',
name: 'RootTag'
},
@@ -1173,13 +1032,11 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_ROOT_TAG
name: 'rootTag',
optional: false,
typeAnnotation: {
nullable: false,
type: 'ReservedFunctionValueTypeAnnotation',
name: 'RootTag'
}
}
],
nullable: false
]
}
}
]
@@ -1203,21 +1060,14 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_SIMPLE_O
optional: false,
typeAnnotation: {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation: {
nullable: false,
type: 'GenericObjectTypeAnnotation'
},
returnTypeAnnotation: { type: 'GenericObjectTypeAnnotation' },
params: [
{
name: 'o',
optional: false,
typeAnnotation: {
nullable: false,
type: 'GenericObjectTypeAnnotation'
}
typeAnnotation: { type: 'GenericObjectTypeAnnotation' }
}
],
nullable: false
]
}
}
]
@@ -10,9 +10,13 @@
import type {
ReservedFunctionValueTypeName,
NativeModuleReturnTypeAnnotation,
NativeModuleBaseTypeAnnotation,
NativeModuleSchema,
NativeModuleParamTypeAnnotation,
} from '../../../../CodegenSchema';
const {parseString} = require('../../index.js');
const {unwrapNullable} = require('../utils');
const {
FlowGenericNotTypeParameterizedParserError,
UnrecognizedFlowTypeAnnotationParserError,
@@ -58,14 +62,16 @@ function expectAnimalTypeAliasToExist(module: NativeModuleSchema) {
expect(animalAlias).not.toBe(null);
invariant(animalAlias != null, '');
expect(animalAlias.type).toBe('ObjectTypeAnnotation');
expect(animalAlias.nullable).toBe(false);
expect(animalAlias.properties.length).toBe(1);
expect(animalAlias.properties[0].name).toBe('name');
expect(animalAlias.properties[0].optional).toBe(false);
expect(animalAlias.properties[0].typeAnnotation.type).toBe(
'StringTypeAnnotation',
const [typeAnnotation, nullable] = unwrapNullable(
animalAlias.properties[0].typeAnnotation,
);
expect(animalAlias.properties[0].typeAnnotation.nullable).toBe(false);
expect(typeAnnotation.type).toBe('StringTypeAnnotation');
expect(nullable).toBe(false);
}
describe('Flow Module Parser', () => {
@@ -133,7 +139,10 @@ describe('Flow Module Parser', () => {
return `${paramName}: ${paramType}`;
}
function parseParamType(paramName: string, paramType: string) {
function parseParamType(
paramName: string,
paramType: string,
): [NativeModuleParamTypeAnnotation, NativeModuleSchema] {
const module = parseModule(`
import type {TurboModule} from 'RCTExport';
import * as TurboModuleRegistry from 'TurboModuleRegistry';
@@ -147,13 +156,18 @@ describe('Flow Module Parser', () => {
`);
expect(module.properties[0]).not.toBe(null);
const param = module.properties[0].typeAnnotation.params[0];
const param = unwrapNullable(module.properties[0].typeAnnotation)[0]
.params[0];
expect(param).not.toBe(null);
expect(param.name).toBe(paramName);
expect(param.optional).toBe(optional);
expect(param.typeAnnotation.nullable).toBe(nullable);
const [
paramTypeAnnotation,
isParamTypeAnnotationNullable,
] = unwrapNullable(param.typeAnnotation);
expect(isParamTypeAnnotationNullable).toBe(nullable);
return [param, module];
return [paramTypeAnnotation, module];
}
describe(
@@ -174,15 +188,15 @@ describe('Flow Module Parser', () => {
describe('Primitive types', () => {
PRIMITIVES.forEach(([FLOW_TYPE, PARSED_TYPE_NAME]) => {
it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} primitive parameter of type '${FLOW_TYPE}'`, () => {
const [param] = parseParamType('arg', FLOW_TYPE);
expect(param.typeAnnotation.type).toBe(PARSED_TYPE_NAME);
const [paramTypeAnnotation] = parseParamType('arg', FLOW_TYPE);
expect(paramTypeAnnotation.type).toBe(PARSED_TYPE_NAME);
});
});
});
it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of type 'Object'`, () => {
const [param] = parseParamType('arg', 'Object');
expect(param.typeAnnotation.type).toBe(
const [paramTypeAnnotation] = parseParamType('arg', 'Object');
expect(paramTypeAnnotation.type).toBe(
'GenericObjectTypeAnnotation',
);
});
@@ -190,18 +204,18 @@ describe('Flow Module Parser', () => {
describe('Reserved Types', () => {
RESERVED_FUNCTION_VALUE_TYPE_NAME.forEach(FLOW_TYPE => {
it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter of reserved type '${FLOW_TYPE}'`, () => {
const [param] = parseParamType('arg', FLOW_TYPE);
const [paramTypeAnnotation] = parseParamType('arg', FLOW_TYPE);
expect(param.typeAnnotation.type).toBe(
expect(paramTypeAnnotation.type).toBe(
'ReservedFunctionValueTypeAnnotation',
);
invariant(
param.typeAnnotation.type ===
paramTypeAnnotation.type ===
'ReservedFunctionValueTypeAnnotation',
'Param must be a Reserved type',
);
expect(param.typeAnnotation.name).toBe(FLOW_TYPE);
expect(paramTypeAnnotation.name).toBe(FLOW_TYPE);
});
});
});
@@ -219,21 +233,25 @@ describe('Flow Module Parser', () => {
function parseParamArrayElementType(
paramName: string,
paramType: string,
) {
const [param, module] = parseParamType(
): [NativeModuleBaseTypeAnnotation, NativeModuleSchema] {
const [paramTypeAnnotation, module] = parseParamType(
paramName,
`Array<${paramType}>`,
);
expect(param.typeAnnotation.type).toBe('ArrayTypeAnnotation');
invariant(
param.typeAnnotation.type === 'ArrayTypeAnnotation',
'',
);
expect(paramTypeAnnotation.type).toBe('ArrayTypeAnnotation');
invariant(paramTypeAnnotation.type === 'ArrayTypeAnnotation', '');
expect(param.typeAnnotation.elementType).not.toBe(null);
invariant(param.typeAnnotation.elementType != null, '');
return [param.typeAnnotation.elementType, module];
expect(paramTypeAnnotation.elementType).not.toBe(null);
invariant(paramTypeAnnotation.elementType != null, '');
const [
elementType,
isElementTypeNullable,
] = unwrapNullable<NativeModuleBaseTypeAnnotation>(
paramTypeAnnotation.elementType,
);
expect(isElementTypeNullable).toBe(false);
return [elementType, module];
}
// TODO: Do we support nullable element types?
@@ -304,35 +322,44 @@ describe('Flow Module Parser', () => {
expect(properties[0]).not.toBe(null);
expect(properties[0].name).toBe('foo');
expect(properties[0].typeAnnotation).not.toBe(null);
expect(properties[0].typeAnnotation?.type).toBe(
'StringTypeAnnotation',
const [typeAnnotation, isPropertyNullable] = unwrapNullable(
properties[0].typeAnnotation,
);
expect(properties[0].typeAnnotation?.nullable).toBe(true);
expect(typeAnnotation.type).toBe('StringTypeAnnotation');
expect(isPropertyNullable).toBe(true);
expect(properties[0].optional).toBe(false);
});
});
it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some type alias`, () => {
const [param, module] = parseParamType('arg', 'Animal');
expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation');
const [paramTypeAnnotation, module] = parseParamType(
'arg',
'Animal',
);
expect(paramTypeAnnotation.type).toBe('TypeAliasTypeAnnotation');
invariant(
param.typeAnnotation.type === 'TypeAliasTypeAnnotation',
paramTypeAnnotation.type === 'TypeAliasTypeAnnotation',
'',
);
expect(param.typeAnnotation.name).toBe('Animal');
expect(paramTypeAnnotation.name).toBe('Animal');
expectAnimalTypeAliasToExist(module);
});
it(`should parse methods that have ${PARAM_TYPE_DESCRIPTION} parameter type of some type alias that points to another type alias`, () => {
const [param, module] = parseParamType('arg', 'AnimalPointer');
expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation');
const [paramTypeAnnotation, module] = parseParamType(
'arg',
'AnimalPointer',
);
expect(paramTypeAnnotation.type).toBe('TypeAliasTypeAnnotation');
invariant(
param.typeAnnotation.type === 'TypeAliasTypeAnnotation',
paramTypeAnnotation.type === 'TypeAliasTypeAnnotation',
'',
);
expect(param.typeAnnotation.name).toBe('Animal');
expect(paramTypeAnnotation.name).toBe('Animal');
expectAnimalTypeAliasToExist(module);
});
@@ -354,18 +381,23 @@ describe('Flow Module Parser', () => {
`);
expect(module.properties[0]).not.toBe(null);
const param = module.properties[0].typeAnnotation.params[0];
const param = unwrapNullable(module.properties[0].typeAnnotation)[0]
.params[0];
expect(param.name).toBe('arg');
expect(param.optional).toBe(optional);
// The TypeAliasAnnotation is called Animal, and is nullable
expect(param.typeAnnotation.type).toBe('TypeAliasTypeAnnotation');
const [
paramTypeAnnotation,
isParamTypeAnnotationNullable,
] = unwrapNullable(param.typeAnnotation);
expect(paramTypeAnnotation.type).toBe('TypeAliasTypeAnnotation');
invariant(
param.typeAnnotation.type === 'TypeAliasTypeAnnotation',
paramTypeAnnotation.type === 'TypeAliasTypeAnnotation',
'',
);
expect(param.typeAnnotation.name).toBe('Animal');
expect(param.typeAnnotation.nullable).toBe(true);
expect(paramTypeAnnotation.name).toBe('Animal');
expect(isParamTypeAnnotationNullable).toBe(true);
// The Animal type alias RHS is valid, and non-null
expectAnimalTypeAliasToExist(module);
@@ -402,19 +434,26 @@ describe('Flow Module Parser', () => {
function parseParamTypeObjectLiteralProp(
propName: string,
propType: string,
) {
const [param, module] = parseParamType(
): [
$ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: NativeModuleBaseTypeAnnotation,
}>,
NativeModuleSchema,
] {
const [paramTypeAnnotation, module] = parseParamType(
'arg',
`{|${annotateProp(propName, propType)}|}`,
);
expect(param.typeAnnotation.type).toBe('ObjectTypeAnnotation');
expect(paramTypeAnnotation.type).toBe('ObjectTypeAnnotation');
invariant(
param.typeAnnotation.type === 'ObjectTypeAnnotation',
paramTypeAnnotation.type === 'ObjectTypeAnnotation',
'',
);
const {properties} = param.typeAnnotation;
const {properties} = paramTypeAnnotation;
expect(properties).not.toBe(null);
invariant(properties != null, '');
@@ -422,16 +461,19 @@ describe('Flow Module Parser', () => {
expect(properties.length).toBe(1);
expect(properties[0].name).toBe(propName);
expect(properties[0].optional).toBe(isPropOptional);
expect(properties[0].typeAnnotation).not.toBe(null);
expect(properties[0].typeAnnotation.nullable).toBe(
isPropNullable,
);
invariant(properties[0].typeAnnotation != null, '');
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(propertyTypeAnnotation).not.toBe(null);
expect(isPropertyTypeAnnotationNullable).toBe(isPropNullable);
return [
{
...properties[0],
typeAnnotation: properties[0].typeAnnotation,
typeAnnotation: propertyTypeAnnotation,
},
module,
];
@@ -504,7 +546,7 @@ describe('Flow Module Parser', () => {
function parseArrayElementType(
propName: string,
arrayElementType: string,
) {
): [NativeModuleBaseTypeAnnotation, NativeModuleSchema] {
const [property, module] = parseParamTypeObjectLiteralProp(
'propName',
`Array<${arrayElementType}>`,
@@ -517,9 +559,21 @@ describe('Flow Module Parser', () => {
'',
);
const {elementType} = property.typeAnnotation;
expect(elementType).not.toBe(null);
invariant(elementType != null, '');
const {
elementType: nullableElementType,
} = property.typeAnnotation;
expect(nullableElementType).not.toBe(null);
invariant(nullableElementType != null, '');
const [
elementType,
isElementTypeNullable,
] = unwrapNullable<NativeModuleBaseTypeAnnotation>(
nullableElementType,
);
expect(isElementTypeNullable).toBe(false);
return [elementType, module];
}
@@ -595,10 +649,16 @@ describe('Flow Module Parser', () => {
expect(properties[0]).not.toBe(null);
expect(properties[0].name).toBe('foo');
expect(properties[0].typeAnnotation).not.toBe(null);
expect(properties[0].typeAnnotation?.type).toBe(
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(propertyTypeAnnotation.type).toBe(
'StringTypeAnnotation',
);
expect(properties[0].typeAnnotation?.nullable).toBe(true);
expect(isPropertyTypeAnnotationNullable).toBe(true);
expect(properties[0].optional).toBe(false);
});
});
@@ -623,11 +683,16 @@ describe('Flow Module Parser', () => {
expect(properties[0]).not.toBe(null);
expect(properties[0].name).toBe('foo');
expect(properties[0].typeAnnotation).not.toBe(null);
expect(properties[0].typeAnnotation?.type).toBe(
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(propertyTypeAnnotation.type).toBe(
'StringTypeAnnotation',
);
expect(properties[0].typeAnnotation?.nullable).toBe(true);
expect(isPropertyTypeAnnotationNullable).toBe(true);
expect(properties[0].optional).toBe(false);
});
@@ -668,10 +733,19 @@ describe('Flow Module Parser', () => {
`);
expect(module.properties[0]).not.toBe(null);
const {returnTypeAnnotation} = module.properties[0].typeAnnotation;
expect(returnTypeAnnotation).not.toBe(null);
const [
functionTypeAnnotation,
isFunctionTypeAnnotationNullable,
] = unwrapNullable(module.properties[0].typeAnnotation);
expect(isFunctionTypeAnnotationNullable).toBe(false);
const [
returnTypeAnnotation,
isReturnTypeAnnotationNullable,
] = unwrapNullable(functionTypeAnnotation.returnTypeAnnotation);
expect(returnTypeAnnotation.type).toBe('VoidTypeAnnotation');
expect(returnTypeAnnotation.nullable).toBe(false);
expect(isReturnTypeAnnotationNullable).toBe(false);
});
[true, false].forEach(IS_RETURN_TYPE_NULLABLE => {
@@ -681,7 +755,9 @@ describe('Flow Module Parser', () => {
const annotateRet = retType =>
IS_RETURN_TYPE_NULLABLE ? `?${retType}` : retType;
function parseReturnType(flowType: string) {
function parseReturnType(
flowType: string,
): [NativeModuleReturnTypeAnnotation, NativeModuleSchema] {
const module = parseModule(`
import type {TurboModule} from 'RCTExport';
import * as TurboModuleRegistry from 'TurboModuleRegistry';
@@ -695,9 +771,18 @@ describe('Flow Module Parser', () => {
`);
expect(module.properties[0]).not.toBe(null);
const {returnTypeAnnotation} = module.properties[0].typeAnnotation;
expect(returnTypeAnnotation).not.toBe(null);
expect(returnTypeAnnotation.nullable).toBe(IS_RETURN_TYPE_NULLABLE);
const [
functionTypeAnnotation,
isFunctionTypeAnnotationNullable,
] = unwrapNullable(module.properties[0].typeAnnotation);
expect(isFunctionTypeAnnotationNullable).toBe(false);
const [
returnTypeAnnotation,
isReturnTypeAnnotationNullable,
] = unwrapNullable(functionTypeAnnotation.returnTypeAnnotation);
expect(isReturnTypeAnnotationNullable).toBe(IS_RETURN_TYPE_NULLABLE);
return [returnTypeAnnotation, module];
}
@@ -749,7 +834,9 @@ describe('Flow Module Parser', () => {
);
});
function parseArrayElementReturnType(flowType: string) {
function parseArrayElementReturnType(
flowType: string,
): [NativeModuleBaseTypeAnnotation, NativeModuleSchema] {
const [returnTypeAnnotation, module] = parseReturnType(
'Array' + (flowType != null ? `<${flowType}>` : ''),
);
@@ -759,10 +846,19 @@ describe('Flow Module Parser', () => {
'',
);
const {elementType} = returnTypeAnnotation;
const arrayTypeAnnotation = returnTypeAnnotation;
const {elementType} = arrayTypeAnnotation;
expect(elementType).not.toBe(null);
invariant(elementType != null, '');
return [elementType, module];
const [
elementTypeAnnotation,
isElementTypeAnnotation,
] = unwrapNullable<NativeModuleBaseTypeAnnotation>(elementType);
expect(isElementTypeAnnotation).toBe(false);
return [elementTypeAnnotation, module];
}
// TODO: Do we support nullable element types?
@@ -824,10 +920,14 @@ describe('Flow Module Parser', () => {
expect(properties[0]).not.toBe(null);
expect(properties[0].name).toBe('foo');
expect(properties[0].typeAnnotation).not.toBe(null);
expect(properties[0].typeAnnotation?.type).toBe(
'StringTypeAnnotation',
);
expect(properties[0].typeAnnotation?.nullable).toBe(true);
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(propertyTypeAnnotation.type).toBe('StringTypeAnnotation');
expect(isPropertyTypeAnnotationNullable).toBe(true);
expect(properties[0].optional).toBe(false);
});
});
@@ -903,7 +1003,14 @@ describe('Flow Module Parser', () => {
function parseObjectLiteralReturnTypeProp(
propName: string,
propType: string,
) {
): [
$ReadOnly<{
name: string,
optional: boolean,
typeAnnotation: NativeModuleBaseTypeAnnotation,
}>,
NativeModuleSchema,
] {
const [returnTypeAnnotation, module] = parseReturnType(
`{|${annotateProp(propName, propType)}|}`,
);
@@ -923,13 +1030,18 @@ describe('Flow Module Parser', () => {
const property = properties[0];
expect(property.name).toBe(propName);
expect(property.optional).toBe(optional);
expect(property.typeAnnotation).not.toBe(null);
expect(property.typeAnnotation?.nullable).toBe(nullable);
invariant(property.typeAnnotation != null, '');
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(property.typeAnnotation);
expect(propertyTypeAnnotation).not.toBe(null);
expect(isPropertyTypeAnnotationNullable).toBe(nullable);
return [
{
...property,
typeAnnotation: property.typeAnnotation,
typeAnnotation: propertyTypeAnnotation,
},
module,
];
@@ -1010,7 +1122,7 @@ describe('Flow Module Parser', () => {
function parseArrayElementType(
propName: string,
arrayElementType: string,
) {
): [NativeModuleBaseTypeAnnotation, NativeModuleSchema] {
const [
property,
module,
@@ -1027,9 +1139,20 @@ describe('Flow Module Parser', () => {
'',
);
const {elementType} = property.typeAnnotation;
expect(elementType).not.toBe(null);
invariant(elementType != null, '');
const {
elementType: nullableElementType,
} = property.typeAnnotation;
expect(nullableElementType).not.toBe(null);
invariant(nullableElementType != null, '');
const [
elementType,
isElementTypeNullable,
] = unwrapNullable<NativeModuleBaseTypeAnnotation>(
nullableElementType,
);
expect(isElementTypeNullable).toBe(false);
return [elementType, module];
}
@@ -1105,13 +1228,15 @@ describe('Flow Module Parser', () => {
expect(properties[0].name).toBe('foo');
expect(properties[0].optional).toBe(false);
expect(properties[0].typeAnnotation).not.toBe(null);
invariant(properties[0].typeAnnotation != null, '');
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(properties[0].typeAnnotation.type).toBe(
expect(propertyTypeAnnotation.type).toBe(
'StringTypeAnnotation',
);
expect(properties[0].typeAnnotation.nullable).toBe(true);
expect(isPropertyTypeAnnotationNullable).toBe(true);
});
});
@@ -1138,13 +1263,15 @@ describe('Flow Module Parser', () => {
expect(properties[0].name).toBe('foo');
expect(properties[0].optional).toBe(false);
expect(properties[0].typeAnnotation).not.toBe(null);
invariant(properties[0].typeAnnotation != null, '');
const [
propertyTypeAnnotation,
isPropertyTypeAnnotationNullable,
] = unwrapNullable(properties[0].typeAnnotation);
expect(properties[0].typeAnnotation.type).toBe(
expect(propertyTypeAnnotation.type).toBe(
'StringTypeAnnotation',
);
expect(properties[0].typeAnnotation.nullable).toBe(true);
expect(isPropertyTypeAnnotationNullable).toBe(true);
});
it(`should parse methods that have ${RETURN_TYPE_DESCRIPTION} return type of an object literal with ${PROP_TYPE_DESCRIPTION} prop of some type alias`, () => {
@@ -16,10 +16,14 @@ import type {
NativeModuleAliasMap,
NativeModuleSchema,
NativeModuleFunctionTypeAnnotation,
NativeModuleArrayTypeAnnotation,
NativeModuleBaseTypeAnnotation,
Nullable,
} from '../../../CodegenSchema.js';
import type {TypeDeclarationMap} from '../utils.js';
const {resolveTypeAnnotation} = require('../utils.js');
const {unwrapNullable, wrapNullable} = require('./utils');
const {
FlowGenericNotTypeParameterizedParserError,
FlowGenericTypeParameterCountMismatchParserError,
@@ -39,7 +43,7 @@ function translateTypeAnnotation(
flowTypeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
aliasMap: {...NativeModuleAliasMap},
): NativeModuleTypeAnnotation {
): Nullable<NativeModuleTypeAnnotation> {
const {
nullable,
typeAnnotation,
@@ -49,21 +53,21 @@ function translateTypeAnnotation(
switch (typeAnnotation.type) {
case 'GenericTypeAnnotation': {
switch (typeAnnotation.id.name) {
case 'RootTag':
return {
nullable,
case 'RootTag': {
return wrapNullable(nullable, {
type: 'ReservedFunctionValueTypeAnnotation',
name: 'RootTag',
};
});
}
case 'Promise': {
assertGenericTypeAnnotationHasExactlyOneTypeParameter(
moduleName,
typeAnnotation,
);
return {
nullable,
return wrapNullable(nullable, {
type: 'PromiseTypeAnnotation',
};
});
}
case 'Array':
case '$ReadOnlyArray': {
@@ -78,11 +82,14 @@ function translateTypeAnnotation(
* invalid Array ElementTypes. Then, make the elementType a required
* parameter.
*/
const elementType = translateTypeAnnotation(
moduleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
const [elementType, isElementTypeNullable] = unwrapNullable(
translateTypeAnnotation(
moduleName,
typeAnnotation.typeParameters.params[0],
types,
aliasMap,
),
);
invariant(
@@ -99,16 +106,18 @@ function translateTypeAnnotation(
`${typeAnnotation.id.name} element type cannot be a function.`,
);
return {
nullable,
const finalTypeAnnotation: NativeModuleArrayTypeAnnotation<
Nullable<NativeModuleBaseTypeAnnotation>,
> = {
type: 'ArrayTypeAnnotation',
elementType: elementType,
elementType: wrapNullable(isElementTypeNullable, elementType),
};
return wrapNullable(nullable, finalTypeAnnotation);
} catch (ex) {
return {
nullable,
return wrapNullable(nullable, {
type: 'ArrayTypeAnnotation',
};
});
}
}
case '$ReadOnly': {
@@ -124,34 +133,29 @@ function translateTypeAnnotation(
);
}
case 'Stringish': {
return {
nullable,
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
};
});
}
case 'Int32': {
return {
nullable,
return wrapNullable(nullable, {
type: 'Int32TypeAnnotation',
};
});
}
case 'Double': {
return {
nullable,
return wrapNullable(nullable, {
type: 'DoubleTypeAnnotation',
};
});
}
case 'Float': {
return {
nullable,
return wrapNullable(nullable, {
type: 'FloatTypeAnnotation',
};
});
}
case 'Object': {
return {
nullable,
return wrapNullable(nullable, {
type: 'GenericObjectTypeAnnotation',
};
});
}
default: {
throw new UnrecognizedFlowGenericParserError(
@@ -162,7 +166,7 @@ function translateTypeAnnotation(
}
}
case 'ObjectTypeAnnotation': {
const objectTypeAnnotationPartial = {
const objectTypeAnnotation = {
type: 'ObjectTypeAnnotation',
properties: typeAnnotation.properties.map(property => {
const {optional} = property;
@@ -180,19 +184,13 @@ function translateTypeAnnotation(
};
if (!typeAliasResolutionStatus.successful) {
return {
nullable,
...objectTypeAnnotationPartial,
};
return wrapNullable(nullable, objectTypeAnnotation);
}
/**
* All aliases RHS are required.
*/
aliasMap[typeAliasResolutionStatus.aliasName] = {
nullable: false,
...objectTypeAnnotationPartial,
};
aliasMap[typeAliasResolutionStatus.aliasName] = objectTypeAnnotation;
/**
* Nullability of type aliases is transitive.
@@ -223,43 +221,40 @@ function translateTypeAnnotation(
* Hence, it's better to manage nullability within the actual TypeAliasTypeAnnotation nodes, and not the
* associated ObjectTypeAnnotations.
*/
return {
nullable: nullable,
return wrapNullable(nullable, {
type: 'TypeAliasTypeAnnotation',
name: typeAliasResolutionStatus.aliasName,
};
});
}
case 'BooleanTypeAnnotation': {
return {
nullable,
return wrapNullable(nullable, {
type: 'BooleanTypeAnnotation',
};
});
}
case 'NumberTypeAnnotation': {
return {
nullable,
return wrapNullable(nullable, {
type: 'NumberTypeAnnotation',
};
});
}
case 'VoidTypeAnnotation': {
return {
nullable,
return wrapNullable(nullable, {
type: 'VoidTypeAnnotation',
};
});
}
case 'StringTypeAnnotation': {
return {
nullable,
return wrapNullable(nullable, {
type: 'StringTypeAnnotation',
};
});
}
case 'FunctionTypeAnnotation': {
return translateFunctionTypeAnnotation(
moduleName,
typeAnnotation,
types,
return wrapNullable(
nullable,
aliasMap,
translateFunctionTypeAnnotation(
moduleName,
typeAnnotation,
types,
aliasMap,
),
);
}
default: {
@@ -305,7 +300,6 @@ function translateFunctionTypeAnnotation(
// TODO(T71778680): This is a FunctionTypeAnnotation. Type this.
flowFunctionTypeAnnotation: $FlowFixMe,
types: TypeDeclarationMap,
nullable: boolean,
aliasMap: {...NativeModuleAliasMap},
): NativeModuleFunctionTypeAnnotation {
const params: Array<NativeModuleMethodParamSchema> = [];
@@ -315,11 +309,13 @@ function translateFunctionTypeAnnotation(
}
const paramName = flowParam.name.name;
const paramTypeAnnotation = translateTypeAnnotation(
moduleName,
flowParam.typeAnnotation,
types,
aliasMap,
const [paramTypeAnnotation, isParamTypeAnnotationNullable] = unwrapNullable(
translateTypeAnnotation(
moduleName,
flowParam.typeAnnotation,
types,
aliasMap,
),
);
invariant(
@@ -335,15 +331,20 @@ function translateFunctionTypeAnnotation(
params.push({
name: flowParam.name.name,
optional: flowParam.optional,
typeAnnotation: paramTypeAnnotation,
typeAnnotation: wrapNullable(
isParamTypeAnnotationNullable,
paramTypeAnnotation,
),
});
}
const returnTypeAnnotation = translateTypeAnnotation(
moduleName,
flowFunctionTypeAnnotation.returnType,
types,
aliasMap,
const [returnTypeAnnotation, isReturnTypeAnnotationNullable] = unwrapNullable(
translateTypeAnnotation(
moduleName,
flowFunctionTypeAnnotation.returnType,
types,
aliasMap,
),
);
invariant(
@@ -353,9 +354,11 @@ function translateFunctionTypeAnnotation(
return {
type: 'FunctionTypeAnnotation',
returnTypeAnnotation,
returnTypeAnnotation: wrapNullable(
isReturnTypeAnnotationNullable,
returnTypeAnnotation,
),
params,
nullable,
};
}
@@ -385,12 +388,9 @@ function buildPropertySchema(
return {
name: methodName,
optional: property.optional,
typeAnnotation: translateFunctionTypeAnnotation(
moduleName,
value,
types,
typeAnnotation: wrapNullable(
nullable,
aliasMap,
translateFunctionTypeAnnotation(moduleName, value, types, aliasMap),
),
};
}
@@ -0,0 +1,45 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {
NativeModuleTypeAnnotation,
Nullable,
} from '../../../CodegenSchema.js';
function unwrapNullable<+T: NativeModuleTypeAnnotation>(
x: Nullable<T>,
): [T, boolean] {
if (x.type === 'NullableTypeAnnotation') {
return [x.typeAnnotation, true];
}
return [x, false];
}
function wrapNullable<+T: NativeModuleTypeAnnotation>(
nullable: boolean,
typeAnnotation: T,
): Nullable<T> {
if (!nullable) {
return typeAnnotation;
}
return {
type: 'NullableTypeAnnotation',
typeAnnotation,
};
}
module.exports = {
unwrapNullable,
wrapNullable,
};