Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49349
Follow-up on D69454101 to add more test coverage for `$ReadOnlyArray<UnsafeMixed>` as a component prop. The new type was missing from the CodegenSchema, which revealed some gaps in tests.
Changelog: [Internal]
Reviewed By: fabriziocucci
Differential Revision: D69488035
fbshipit-source-id: 19895e55e5ec4d89a790f1c388de9eea025a316c
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48476
Command param array types were generating invalid schemas due to untyped parser code. The invalid schemas occurred for any alias type, including custom objects and basics like Int32. This was also inconsistent between Flow and TypeScript.
We already had one component utilizing this issue, so this just codifies that support into the schema so it reflects reality. This is only a partial solution. The more full solution would be to fully encode the custom types in the schemas like we do for Native Modules.
# More Information:
Tl;dr, DebuggingOverlay is abusing a FlowFixMe in codegen commands.
## The problem:
The [CodegenSchema](https://www.internalfb.com/code/fbsource/[d3ab2f79b377]/xplat/js/react-native-github/packages/react-native-codegen/src/CodegenSchema.js?lines=220) should be the source of truth for anything that can be in the schema. If something is in the schema that isn't allowed by the types, that's a bug. We have a bug. I'm adding compat-check support for components and it's blowing up on prod schemas because DebuggingOverlay causes an invalid schema.
## The details:
Support for Arrays as arguments in commands was added to the Codegen in D51866557. [Code Pointer](https://fburl.com/code/8yy1rm0p)
The intention appears to be to support arrays of primitives. There is a TODO for supporting complex types.
```
interface NativeCommands {
+addOverlays: (
viewRef: React.ElementRef<NativeType>,
overlayColorsReadOnly: $ReadOnlyArray<string>,
)
}
```
This support was added to TypeScript in D52046165 where it types the allowed Array arguments to be only
```
{
readonly type: 'ArrayTypeAnnotation';
readonly elementType:
| Int32TypeAnnotation
| DoubleTypeAnnotation
| FloatTypeAnnotation
| BooleanTypeAnnotation
| StringTypeAnnotation
};
```
However, because the Parsers are treating the input type as `any`, it isn't safe to pass through an input value into the schema as Flow won't catch mismatches.
The Flow parser just passes it through:
```
{
type: 'ArrayTypeAnnotation',
elementType: {
// TODO: T172453752 support complex type annotation for array element
type: paramValue.typeParameters.params[0].type,
}
```
Whereas the TypeScript parser has the more correct behavior of validating the inputs and returning specific outputs. Unfortunately, the return type is also typed here as $FlowFixMe, losing most of the benefits.
```
function getPrimitiveTypeAnnotation(type: string): $FlowFixMe {
switch (type) {
case 'Int32':
return {
type: 'Int32TypeAnnotation',
};
case 'Double':
return {
type: 'DoubleTypeAnnotation',
};
case 'Float':
return {
type: 'FloatTypeAnnotation',
};
case 'TSBooleanKeyword':
return {
type: 'BooleanTypeAnnotation',
};
case 'Stringish':
case 'TSStringKeyword':
return {
type: 'StringTypeAnnotation',
};
default:
throw new Error(`Unknown primitive type "${type}"`);
}
}
```
[DebuggingOverlay](https://fburl.com/code/zfe3ipq7) is abusing this gap in the Flow parser by sticking an Array of Objects in.
```
export type ElementRectangle = {
x: number,
y: number,
width: number,
height: number,
};
...
+highlightElements: (
viewRef: React.ElementRef<DebuggingOverlayNativeComponentType>,
elements: $ReadOnlyArray<ElementRectangle>,
) => void;
...
```
This isn't allowed in the schema, but it seems to fall through the holes of the flow parser and generators.
The resulting schema from Flow is this. Note the GenericTypeAnnotation which isn't allowed to be in the schema.
```
{
"name": "highlightElements",
"optional": false,
"typeAnnotation": {
"type": "FunctionTypeAnnotation",
"params": [
{
"name": "elements",
"optional": false,
"typeAnnotation": {
"type": "ArrayTypeAnnotation",
"elementType": {
"type": "GenericTypeAnnotation"
}
}
}
],
"returnTypeAnnotation": {
"type": "VoidTypeAnnotation"
}
},
```
The TypeScript parser fails with `Error: Unsupported type annotation: GenericTypeAnnotation`.
The generators don't seem to check beyond the ArrayTypeAnnotation so they fall through to generating generic arrays.
```
// ios
elements:(const NSArray *)elements
// android
ReadableArray locations
```
## So how do I fix this?
I think there are a couple of different options here. The key problem is that the Schema types need to represent reality of what can be in the schema.
1. We revert DebuggingOverlay to not use features that aren't supported (I assume nobody would be happy with this, but the change shouldn't have been made in the first place)
2. **(This is the approach taken in this diff)** We add MixedTypeAnnotation to the allowed types in Command arrays and have it generate that and add official support for that to the TypeScript parser as well. That is probably the quickest and easiest approach. It leaves the same type unsafety we have today on the native side.
3. NativeModules seem to have a lot more complex type safety here. They persist the alias type in the schema so that the CompatCheck can check them on changes. And then in C++ they generate structs and RCTConvert functions although for Java and ObjC it looks like they just use the same untyped native code. The matching approach here would be to add `aliasMap` and the whole data to the schema for commands, use that for the compat check, and still generate the same unsafe native code.
```
export type ObjectAlias = {|
x: number,
y: number,
|};
export interface Spec extends TurboModule {
+getAlias: (a: ObjectAlias) => string;
}
```
stores the ObjectAlias in the schema
```
{
"aliasMap": {
"ObjectAlias": {
"type": "ObjectTypeAnnotation",
"properties": [
{
"name": "x",
"optional": false,
"typeAnnotation": {
"type": "NumberTypeAnnotation"
}
},
{
"name": "y",
"optional": false,
"typeAnnotation": {
"type": "NumberTypeAnnotation"
}
},
]
}
},
"spec": {
"methods": [
{
"name": "getAlias",
"optional": false,
"typeAnnotation": {
"type": "FunctionTypeAnnotation",
"returnTypeAnnotation": {
"type": "StringTypeAnnotation"
},
"params": [
{
"name": "a",
"optional": false,
"typeAnnotation": {
"type": "TypeAliasTypeAnnotation",
"name": "ObjectAlias"
}
}
]
}
}
]
}
}
```
and then generates the appropriate structs on the native side and generates [this](https://www.internalfb.com/code/fbsource/[d3ab2f79b377]/xplat/js/react-native-github/packages/react-native-codegen/src/generators/modules/__tests__/__snapshots__/GenerateModuleHObjCpp-test.js.snap?lines=818)
```
Reviewed By: hoxyq
Differential Revision: D67806838
fbshipit-source-id: 31f20455c816fdb6b1a86f8f9d0f6f7d0a452754
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48474
The previous definition said that you could put prop types into commands, which definitely isn't allowed.
For example, the schema would have allowed `WithDefault` types.
```
interface NativeCommands {
+methodInt: (viewRef: React.ElementRef<NativeType>, a: WithDefault<string, 'hi'>) => void;
}
```
This change separates out the things that are allowed in commands from what's allowed in props.
Commands should be very similar to what's allowed in native modules, but it isn't exact enough to be able to merge those.
## Changelog:
[General][Breaking] - Codegen: Separate component array types and command array types
Reviewed By: cipolleschi
Differential Revision: D67806818
fbshipit-source-id: 58e504fe2e2e5efa612e836b18af22a167e7ae2f
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48343
components store unions as 'StringEnumTypeAnnotation' even though it isn't actually a union, it's a literal.
Native Modules store these as 'StringLiteralTypeAnnotation' so this converges those and reuses the same types.
Changelog: [Internal]
Reviewed By: cipolleschi
Differential Revision: D67427656
fbshipit-source-id: e39028114285588584596012d07db40c117b4b94
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48318
These structures were the same, but the component side didn't use generics and just had duplicates. Making a base one to be shared.
I need to follow up to this and constrain the types that components allow.
Changelog: [Internal]
Reviewed By: javache
Differential Revision: D67371894
fbshipit-source-id: bb1a30fcd0efe6cc567b88bc6f11e7b385bd7c41
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47349
This is needed to be able to recurse into the literals and compare them.
I'm primarily unsure if there is a problem representing doubles/floats as numbers instead of strings though.
Changelog: [Internal]
Reviewed By: makovkastar
Differential Revision: D65284058
fbshipit-source-id: b2de9ed5fb7f079a432c94aaea69027863879909
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47324
I need to reference this type somewhere else, but not an array of the type.
Generally we prefer that all exported types are the object itself, and it used as a member type of arrays when used.
Changelog: [Internal]
Reviewed By: makovkastar
Differential Revision: D65259014
fbshipit-source-id: 35fb5fe03a44bed61ad87337d0fc5c198744c0e9
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47323
This change adds support for number literals as a type.
The codegen already has parsing support for these types
```
+passNumber: (arg: number) => void;
+passString: (arg: string) => void;
+passStringLiteral: (arg: 'A String Literal') => void;
```
This change now also supports
```
+passNumberLiteral: (arg: 4) => void;
```
On the native side this is treated the same as `number`. It could be strengthened in the future.
This is a pre-requisite for number literal unions and enums.
Changelog: [Added] Codegen: Added support for Number literals in native module specs
Reviewed By: makovkastar
Differential Revision: D65249334
fbshipit-source-id: 98b051d2a6bd1ad5cc6473ac88acfcbe82bd5c7d
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46845
Previously, the parser was throwing away the values of a string union when storing it in the schema. It would only store the union as
```
{
type: 'UnionTypeAnnotation',
memberType: 'StringTypeAnnotation'
}
```
We now track the string literals through the parser and store them in the schema:
```
{
type: 'StringLiteralUnionTypeAnnotation',
types: [
{
type: 'StringLiteralTypeAnnotation'
value: 'light'
},
{
type: 'StringLiteralTypeAnnotation'
value: 'dark'
},
],
}
```
We aren't changing the generators, those still just output "string". They could eventually be made smarter.
The value of this is that the compat checker can now error when the union changes.
Changelog: [Internal]
Reviewed By: yungsters
Differential Revision: D63917685
fbshipit-source-id: 34a10e1f1910d2935837a3659f66049fd4473134
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46827
You can now define a native module that takes an argument that is a specific string literal:
Like this:
```
export interface Spec extends TurboModule {
+passString: (arg: string) => void;
+passStringLiteral: (arg: 'A String Literal') => void;
}
```
On the native side, this will still generate `string`, but the schema will now store the string literal, and it will be allowed in JS. This should allow more strict flow / typescript types for modules.
This will also allow the compatibility check to fail if the literal changes.
This is a step towards accepting a union of string literals.
Changelog: [General][Added] Codegen for Native Modules now supports string literals
Reviewed By: GijsWeterings
Differential Revision: D63872440
fbshipit-source-id: e54b97d34af4a3d1af51727db0777f26fb7b778c
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46727
It makes scripts operating on the schema complicated when the elementType might be there or might not. Let's make it required, but VoidTypeAnnotation if it's unknown.
Arguably we shouldn't allow it to be unknown at all, but that's out of scope here.
Changelog: [Internal]
Reviewed By: GijsWeterings
Differential Revision: D63616703
fbshipit-source-id: 290586384b911928e55344aa522bd296f23a074c
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46237
I can't find any uses of this, it's not referenced in any fixtures, and flow and typescript both pass without it.
Changelog: [Internal]
Reviewed By: makovkastar
Differential Revision: D61892355
fbshipit-source-id: 8ebb4da3e104109c740d90c2495dbcc89d3978e5
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/46221
Previously the schema special cased unparseable elementType with elementType just being undefined. This causes issues for logic that requires recursively matching types. Instead of being implicit, this makes them explicitly an AnyTypeAnnotation
Changelog: [Internal]
Reviewed By: makovkastar
Differential Revision: D61825742
fbshipit-source-id: 47bf70d32d21647896d8f5319087378cc8ac8d4f
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/44919
## Changelog:
[Internal] [Fixed] - Call Turbo Module methods 'methods' in the Turbo Module JSON schema
We don't support `properties` on Turbo Modules. We only support methods (even eventEmitters are just methods)
Reviewed By: javache
Differential Revision: D58510557
fbshipit-source-id: 02b1dc93a37b58b47bb9fd94a9658b5a7301bf55
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/44809
Adding react-native-codegen parser support for a new `EventEmitter` property type on C++ Turbo Modules.
It is possible to later expand this feature to other languages (Java, ObjC).
## Characteristics
An `EventEmitter` must:
- be non null:
`EventEmitter<string>` works, `?EventEmitter<string>` does NOT
- have a non null eventType:
`EventEmitter<number>` works, `EventEmitter<?number>` does NOT
- have at most 1 eventType, `void` is possible as well:
`EventEmitter<>` or `EventEmitter<MyObject>` work - `EventEmitter<number, string>` do NOT
- have a concrete eventType, `{}` is not allowed
`EventEmitter<{}>` does NOT work
- be used in `Cxx` Turbo Modules only at this time
## Example
For these 4 eventEmitters in on an RN JS TM spec
```
+onPress: EventEmitter<void>;
+onClick: EventEmitter<string>;
+onChange: EventEmitter<ObjectStruct>;
+onSubmit: EventEmitter<ObjectStruct[]>;
```
We now generate this code:
1.) in the spec based header `{MyModuleName}CxxSpec` in the constructor:
```
... // existing code
eventEmitterMap_["onPress"] = std::make_shared<AsyncEventEmitter<>>();
eventEmitterMap_["onClick"] = std::make_shared<AsyncEventEmitter<OnClickType>>();
eventEmitterMap_["onChange"] = std::make_shared<AsyncEventEmitter<OnChangeType>>();
eventEmitterMap_["onSubmit"] = std::make_shared<AsyncEventEmitter<OnSubmitType>>();
```
2.) as `protected` functions
```
void emitOnPress() {
std::static_pointer_cast<AsyncEventEmitter<>>(delegate_.eventEmitterMap_["onPress"])->emit();
}
void emitOnClick(const OnClickType& value) {
std::static_pointer_cast<AsyncEventEmitter<OnClickType>>(delegate_.eventEmitterMap_["onClick"])->emit(value);
}
void emitOnChange(const OnChangeType& value) {
std::static_pointer_cast<AsyncEventEmitter<OnChangeType>>(delegate_.eventEmitterMap_["onChange"])->emit(value);
}
void emitOnSubmit(const OnSubmitType& value) {
std::static_pointer_cast<AsyncEventEmitter<OnSubmitType>>(delegate_.eventEmitterMap_["onSubmit"])->emit(value);
}
```
## Changelog:
[General] [Added] - Add EventEmitter code-gen support for C++ Turbo Modules
Reviewed By: javache
Differential Revision: D57407871
fbshipit-source-id: 2345cc6dacf0cb0d45f8a374ad9d4cbf8082f9d6
Summary:
Previously we allow `{[key:string]:Something}` in codegen, `Something` is type-checked but thrown away, generating a `GenericObjectTypeAnnotation`.
In this change, `Something` is added to `GenericObjectTypeAnnotation` as an optional field.
For downstream code such as C++ codegen, this change is **backward compatible**. It allows C++ codegen to produce a more precious type optionally.
## Changelog:
[General] [Added] - Recognize dictionary type in codegen
Pull Request resolved: https://github.com/facebook/react-native/pull/37206
Test Plan:
```
yarn jest react-native-codegen
yarn jest react-native-codegen-typescript-test
```
Reviewed By: cipolleschi
Differential Revision: D45563340
Pulled By: dmytrorykun
fbshipit-source-id: 9a9ce36df6ded6d42d35c3dcb6fb0eaca16c4458
Summary:
- Add a typescript project to test `CodegenSchema.d.ts`. More tests for other .d.ts files will be added in future pull requests.
- The build script scans all snapshots from `react-native/codegen`'s typescript frontend and generates .ts files for each snapshot, but they are .gitignore-ed.
- `npm run build` will build these .ts files against `CodegenSchema.d.ts` after generating them.
- A failed jest case is included to ensure CI catch it, it will be removed before merged.
bypass-github-export-checks
## Changelog:
[General] [Added] - Add react-native/codegen-typescript-test to verify .d.ts files in react-native/codegen (1)
Pull Request resolved: https://github.com/facebook/react-native/pull/36562
Test Plan:
`npm run build` in `packages/react-native-codegen-typescript-test` and see all test files appear in `__generated__`.
## Screenshot

Reviewed By: rshest
Differential Revision: D44292277
Pulled By: cipolleschi
fbshipit-source-id: 8d79fe913f9563d64c92aae7c4f4e97a24ae9a21
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/36345
`exactOptionalPropertyTypes` is a TypeScript 4.4+ option set by users which changes behavior of optional properties, to disable accepting explicit `undefined`.
This is not enabled when using `--strict`, and is stricter than Flow, leading to most of the typings having an `| undefined` unique to TypeScript (added with https://github.com/DefinitelyTyped/DefinitelyTyped/commit/694c663a9486dbe7794d5eb894a691ee9ded318a).
We have not always followed this (I have myself previously assumed the two are equivalent). We can enforce that the convention is followed with a plugin `eslint-plugin-redundant-undefined`. This forces us to declare that every optional property accepts an explicit undefined (which Flow would allow). Alternatively, if we do not want to support this, we can enable the existing dtslint rule `no-redundant-undefined`.
Changelog:
[General][Fixed] - Enforce compatibility with `exactOptionalPropertyTypes`
Reviewed By: lunaleaps
Differential Revision: D43700862
fbshipit-source-id: 996094762b28918177521a9471d868ba87f0f263
Summary:
Add minimum necessary .d.ts files to react-native-codegen.
I found .d.ts files will be copied to `lib` so I guess no additional script is needed.
## Changelog
[GENERAL] [CHANGED] - Add minimum necessary .d.ts files to react-native-codegen
Pull Request resolved: https://github.com/facebook/react-native/pull/36102
Test Plan: `npm run build` in `packages/react-native-codegen` and see all .d.ts files appear in `lib`.
Reviewed By: cortinico
Differential Revision: D43157233
Pulled By: cipolleschi
fbshipit-source-id: 6f122f0f4cda693ba22af6dd534e9d34d069ecac