Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-native/Libraries/Alert/Alert.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive';
export type AlertButton = {
text?: string,
// $FlowFixMe[unclear-type]
onPress?: ?((value?: string) => any) | ?Function,
onPress?: ?((value?: string) => any) | ?LegacyFunction,
isPreferred?: boolean,
style?: AlertButtonStyle,
...
Expand Down
15 changes: 5 additions & 10 deletions packages/react-native/Libraries/Animated/AnimatedImplementation.js
Original file line number Diff line number Diff line change
Expand Up @@ -559,12 +559,9 @@ const loopImpl = function (
};

function forkEventImpl(
// $FlowFixMe[unclear-type]
event: ?AnimatedEvent | ?Function,
// $FlowFixMe[unclear-type]
listener: Function,
// $FlowFixMe[unclear-type]
): AnimatedEvent | Function {
event: ?AnimatedEvent | ?LegacyFunction,
listener: LegacyFunction,
): AnimatedEvent | LegacyFunction {
if (!event) {
return listener;
} else if (event instanceof AnimatedEvent) {
Expand All @@ -579,10 +576,8 @@ function forkEventImpl(
}

function unforkEventImpl(
// $FlowFixMe[unclear-type]
event: ?AnimatedEvent | ?Function,
// $FlowFixMe[unclear-type]
listener: Function,
event: ?AnimatedEvent | ?LegacyFunction,
listener: LegacyFunction,
): void {
if (event && event instanceof AnimatedEvent) {
event.__removeListener(listener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,7 @@ class PushNotificationIOS {
*
* See https://reactnative.dev/docs/pushnotificationios#getapplicationiconbadgenumber
*/
// $FlowFixMe[unclear-type]
static getApplicationIconBadgeNumber(callback: Function): void {
static getApplicationIconBadgeNumber(callback: LegacyFunction): void {
invariant(
NativePushNotificationManagerIOS,
'PushNotificationManager is not available.',
Expand Down Expand Up @@ -348,8 +347,7 @@ class PushNotificationIOS {
*
* See https://reactnative.dev/docs/pushnotificationios#getscheduledlocalnotifications
*/
// $FlowFixMe[unclear-type]
static getScheduledLocalNotifications(callback: Function): void {
static getScheduledLocalNotifications(callback: LegacyFunction): void {
invariant(
NativePushNotificationManagerIOS,
'PushNotificationManager is not available.',
Expand All @@ -365,8 +363,7 @@ class PushNotificationIOS {
*/
static addEventListener(
type: PushNotificationEventName,
// $FlowFixMe[unclear-type]
handler: Function,
handler: LegacyFunction,
): void {
invariant(
type === 'notification' ||
Expand Down
4 changes: 1 addition & 3 deletions packages/react-native/Libraries/Utilities/Dimensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ class Dimensions {
*/
static addEventListener(
type: 'change',
/* $FlowFixMe[unclear-type] Callers annotate the payload with assorted
* object shapes that any precise signature would reject. */
handler: Function,
handler: LegacyFunction,
): EventSubscription {
invariant(
type === 'change',
Expand Down
13 changes: 13 additions & 0 deletions packages/react-native/flow/LegacyFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 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
* @format
*/

// The legacy Flow `Function` type, which is `any`. The generated TypeScript API
// (scripts/js-api/build-types) translates this type to `Function`.
declare type LegacyFunction = any;
Original file line number Diff line number Diff line change
@@ -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
*/

const translateSourceFile = require('../../../translateSourceFile');
const replaceLegacyFunctionWithFunction = require('../replaceLegacyFunctionWithFunction');
const {parse, print} = require('flow-transform');

const prettierOptions = {parser: 'babel'};

async function translate(code: string): Promise<string> {
const parsed = await parse(code);
const result = await replaceLegacyFunctionWithFunction(parsed);
return print(result.ast, result.mutatedCode, prettierOptions);
}

describe('replaceLegacyFunctionWithFunction', () => {
test('replaces LegacyFunction in return annotations', async () => {
expect(await translate('function foo(): LegacyFunction {}')).toBe(
'function foo(): Function {}\n',
);
});

test('replaces LegacyFunction in nullable unions', async () => {
expect(
await translate('let foo: ?((value?: string) => any) | ?LegacyFunction;'),
).toBe('let foo: ?((value?: string) => any) | ?Function;\n');
});

test('replaces LegacyFunction in type aliases', async () => {
expect(await translate('type Foo = LegacyFunction;')).toBe(
'type Foo = Function;\n',
);
});

test('preserves unrelated and qualified types', async () => {
const code = 'type Foo = Function | Other.LegacyFunction;';
expect(await translate(code)).toBe(code + '\n');
});

test('preserves Function in the generated TypeScript API', async () => {
const source = `export type Callbacks = {
onPress?: ?((value?: string) => any) | ?LegacyFunction,
listener: LegacyFunction,
};
export function forkEvent(event: ?LegacyFunction): LegacyFunction {
return event;
}`;
const result = await translateSourceFile(source, __filename);
const original = await translateSourceFile(
source.replaceAll('LegacyFunction', 'Function'),
__filename,
);
expect(result).toEqual(original);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* 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
*/

import type {TransformVisitor} from 'flow-transform';
import type {ParseResult} from 'flow-transform/dist/transform/parse';
import type {TransformASTResult} from 'flow-transform/dist/transform/transformAST';

const {transformAST} = require('flow-transform/dist/transform/transformAST');

const visitors: TransformVisitor = context => ({
GenericTypeAnnotation(node): void {
if (node.id.type === 'Identifier' && node.id.name === 'LegacyFunction') {
context.modifyNodeInPlace(node.id, {name: 'Function'});
}
},
});

/**
* Replaces LegacyFunction with Function to preserve the TypeScript API.
*/
async function replaceLegacyFunctionWithFunction(
source: ParseResult,
): Promise<TransformASTResult> {
return transformAST(source, visitors);
}

module.exports = replaceLegacyFunctionWithFunction;
1 change: 1 addition & 0 deletions scripts/js-api/build-types/translateSourceFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const preTransforms: Array<PreTransformFn> = [
require('./transforms/flow/replaceRequiresWithImports'),
require('./transforms/flow/replaceEmptyWithNever'),
require('./transforms/flow/replaceStringishWithString'),
require('./transforms/flow/replaceLegacyFunctionWithFunction'),
require('./transforms/flow/replaceLegacyObjectWithObject'),
require('./transforms/flow/replaceNullablePropertiesWithUndefined'),
require('./transforms/flow/reattachDocComments'),
Expand Down
Loading