Add pre-transform that replaces nullable types with undefined for object types (#50213)

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

Changelog: [Internal]

Reviewed By: huntie

Differential Revision: D71730936

fbshipit-source-id: c126d461fda4ab9758a41421d8300dd92a5cd9af
This commit is contained in:
Jakub Piasecki
2025-03-24 07:21:58 -07:00
committed by Facebook GitHub Bot
parent f760c11f65
commit a4282a7cc3
3 changed files with 122 additions and 0 deletions
@@ -0,0 +1,59 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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-local
* @format
* @oncall react_native
*/
const replaceNullablePropertiesWithUndefined = require('../replaceNullablePropertiesWithUndefined.js');
const {parse, print} = require('hermes-transform');
const prettierOptions = {parser: 'babel'};
async function translate(code: string): Promise<string> {
const parsed = await parse(code);
const result = await replaceNullablePropertiesWithUndefined(parsed);
return print(result.ast, result.mutatedCode, prettierOptions);
}
describe('replaceNullablePropertiesWithUndefined', () => {
test('should not replace nullable with undefined outside object type definition', async () => {
const code = `function foo(arg: ?string) {}`;
const result = await translate(code);
expect(result).toMatchInlineSnapshot(`
"function foo(arg: ?string) {}
"
`);
});
test('should replace nullable with undefined inside object type definition', async () => {
const code = `type Foo = {bar: ?string};`;
const result = await translate(code);
expect(result).toMatchInlineSnapshot(`
"type Foo = { bar: string | undefined };
"
`);
});
test('should replace nullable with undefined inside nested object type definition', async () => {
const code = `type Foo = {bar: {baz: ?string}};`;
const result = await translate(code);
expect(result).toMatchInlineSnapshot(`
"type Foo = { bar: { baz: string | undefined } };
"
`);
});
test('should replace nullable with undefined in function inside object type definition', async () => {
const code = `type Foo = {bar: (?string) => void};`;
const result = await translate(code);
expect(result).toMatchInlineSnapshot(`
"type Foo = { bar: (string | undefined) => void };
"
`);
});
});
@@ -0,0 +1,62 @@
/**
* Copyright (c) Meta Platforms, Inc. and 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-local
* @format
* @oncall react_native
*/
import type {ESNode, TypeAnnotationType} from 'hermes-estree';
import type {TransformVisitor} from 'hermes-transform';
import type {ParseResult} from 'hermes-transform/dist/transform/parse';
import type {TransformASTResult} from 'hermes-transform/dist/transform/transformAST';
const {transformAST} = require('hermes-transform/dist/transform/transformAST');
function isInsideObjectTypeProperty(node: TypeAnnotationType) {
let current: ESNode = node;
while (current) {
if (current.type === 'ObjectTypeProperty') {
return true;
}
current = current.parent;
}
return false;
}
const visitors: TransformVisitor = context => ({
NullableTypeAnnotation(node): void {
if (!isInsideObjectTypeProperty(node)) {
return;
}
// $FlowExpectedError[incompatible-call] - UnionTypeAnnotation is not assignable to NullableTypeAnnotation
context.replaceNode(node, {
type: 'UnionTypeAnnotation',
types: [
node.typeAnnotation,
{
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: 'undefined',
},
},
],
});
},
});
/**
* Replaces all Stringish type references with string
*/
async function replaceNullablePropertiesWithUndefined(
source: ParseResult,
): Promise<TransformASTResult> {
return transformAST(source, visitors);
}
module.exports = replaceNullablePropertiesWithUndefined;
@@ -25,6 +25,7 @@ const preTransforms: Array<PreTransformFn> = [
require('./transforms/replaceRequiresWithImports'),
require('./transforms/replaceEmptyWithNever'),
require('./transforms/replaceStringishWithString'),
require('./transforms/replaceNullablePropertiesWithUndefined'),
];
const postTransforms: Array<PluginObj<mixed>> = [];
const prettierOptions = {parser: 'babel'};