mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
add 'as const' annotations in preparation of natural inference rollout in xplat/js [4/n] (#51488)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/51488 The Flow team is improving the way Flow infers type for primitive literals. Announcement: https://fb.workplace.com/groups/flowlang/permalink/1725180268087629/ This diff prepares the codebase for the new behavior by codemoding `as const` annotations. ## Repro steps 1/ Used steps in D73610163 to produce the code changes. 2/ Reverted files where `flow` errored: ``` flow status --show-all-errors > errors.log node ~/fbsource/fbcode/flow/facebook/error-analyzer.js errors.log | awk -F':' '{ print $1 }' | sort -u | grep -v 'Total Error Count' | xargs hg revert --rev . ``` 3/ Reverted files that did not improve error count in new Flow mode ``` # Run Flow before change ~/fbsource/fbcode/flow/facebook/flowd status --show-all-errors > errors-0.log # Run Flow after change ~/fbsource/fbcode/flow/facebook/flowd status --show-all-errors > errors-1.log # Compute error counts before and after node ~/fbsource/fbcode/flow/facebook/error-analyzer.js errors-0.log | sort > errors-counts-0.log node ~/fbsource/fbcode/flow/facebook/error-analyzer.js errors-1.log | sort > errors-counts-1.log # Revert files with no change in error count comm -12 errors-counts-0.log errors-counts-1.log | awk -F':' '{ print $1 }' | xargs hg revert --rev .~1 ``` ## Note to code owners Due to the large number of errors involved in this rollout, adding `as const` was the most feasible large-scale automated solution. Ideally, a lot of these errors would be fixed by adding other appropriate type annotations. For example instead of annotating ``` type Shape = {type: 'circle', radius: number} | {type: 'square', side: number} | ...; type ShapeKind = 'circle' | 'square' | 'triangle'; const circle = { type: "circle" as const, // <-- annotation added here radius: 42, }; shape.type as ShapeKind; takesShape(circle); ``` a more appropriate annotation would be ``` const circle: Circle = { type: "circle"; radius: 42 }; ... ``` Changelog: [Internal] drop-conflicts Reviewed By: SamChou19815 Differential Revision: D75114154 fbshipit-source-id: 67ee5673816da9625431e2a2466a1e0038386151
This commit is contained in:
committed by
Facebook GitHub Bot
parent
380dd59e80
commit
2a068c6559
+1
-1
@@ -190,7 +190,7 @@ const transform /*: BabelTransformer['transform'] */ = ({
|
||||
try {
|
||||
const babelConfig = {
|
||||
// ES modules require sourceType='module' but OSS may not always want that
|
||||
sourceType: 'unambiguous',
|
||||
sourceType: 'unambiguous' as const,
|
||||
...buildBabelConfig(filename, options, plugins),
|
||||
caller: {name: 'metro', bundler: 'metro', platform: options.platform},
|
||||
ast: true,
|
||||
|
||||
@@ -977,14 +977,14 @@ function buildNativeModulesDiff(
|
||||
);
|
||||
|
||||
const newType = {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: [
|
||||
...newerNativeModule.spec.methods,
|
||||
...newerNativeModule.spec.eventEmitters,
|
||||
],
|
||||
};
|
||||
const oldType = {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: [
|
||||
...olderNativeModule.spec.methods,
|
||||
...olderNativeModule.spec.eventEmitters,
|
||||
@@ -1074,13 +1074,13 @@ function buildNativeComponentsDiff(
|
||||
);
|
||||
|
||||
const newCommands = {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: [command],
|
||||
};
|
||||
const oldCommands =
|
||||
oldCommand != null
|
||||
? {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: [oldCommand],
|
||||
}
|
||||
: null;
|
||||
@@ -1124,7 +1124,7 @@ function buildNativeComponentsDiff(
|
||||
// We have to do this to remove the .defaults from the props and get it into
|
||||
// standard JavaScript shapes.
|
||||
const newConvertedProps = {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: newerComponent.props.map(prop => ({
|
||||
name: prop.name,
|
||||
optional: prop.optional,
|
||||
@@ -1132,7 +1132,7 @@ function buildNativeComponentsDiff(
|
||||
})),
|
||||
};
|
||||
const oldConvertedProps = {
|
||||
type: 'ObjectTypeAnnotation',
|
||||
type: 'ObjectTypeAnnotation' as const,
|
||||
properties: olderComponent.props.map(prop => ({
|
||||
name: prop.name,
|
||||
optional: prop.optional,
|
||||
|
||||
+2
-2
@@ -74,8 +74,8 @@ async function processRNTesterCommandResult(
|
||||
parsed = JSON.parse(line);
|
||||
} catch {
|
||||
parsed = {
|
||||
type: 'console-log',
|
||||
level: 'info',
|
||||
type: 'console-log' as const,
|
||||
level: 'info' as const,
|
||||
message: line,
|
||||
};
|
||||
}
|
||||
|
||||
+2
-2
@@ -331,10 +331,10 @@ function runSpec(spec: Spec): TestCaseResult {
|
||||
spec.implementation();
|
||||
invokeHooks(spec.parentContext, 'afterEachHooks');
|
||||
|
||||
status = 'passed';
|
||||
status = 'passed' as const;
|
||||
} catch (e) {
|
||||
error = e;
|
||||
status = 'failed';
|
||||
status = 'failed' as const;
|
||||
}
|
||||
|
||||
result.status = status;
|
||||
|
||||
+1
-1
@@ -279,7 +279,7 @@ class Modal extends React.Component<ModalProps, ModalState> {
|
||||
: this.props.backdropColor ?? 'white',
|
||||
};
|
||||
|
||||
let animationType = this.props.animationType || 'none';
|
||||
let animationType = this.props.animationType || ('none' as const);
|
||||
|
||||
let presentationStyle = this.props.presentationStyle;
|
||||
if (!presentationStyle) {
|
||||
|
||||
Vendored
+12
-12
@@ -40,11 +40,11 @@ test('passes for identical configs', () => {
|
||||
},
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: {
|
||||
collapsable: true,
|
||||
nativeID: true,
|
||||
collapsable: true as const,
|
||||
nativeID: true as const,
|
||||
style: {
|
||||
height: true,
|
||||
width: true,
|
||||
height: true as const,
|
||||
width: true as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -70,11 +70,11 @@ test('passes for identical configs', () => {
|
||||
},
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: {
|
||||
collapsable: true,
|
||||
nativeID: true,
|
||||
collapsable: true as const,
|
||||
nativeID: true as const,
|
||||
style: {
|
||||
height: true,
|
||||
width: true,
|
||||
height: true as const,
|
||||
width: true as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -190,11 +190,11 @@ test('allows static viewconfigs to have more properties than native viewconfigs'
|
||||
const staticViewConfig = {
|
||||
uiViewClassName: 'RCTView',
|
||||
validAttributes: {
|
||||
collapsable: true,
|
||||
nativeID: true,
|
||||
collapsable: true as const,
|
||||
nativeID: true as const,
|
||||
style: {
|
||||
height: true,
|
||||
width: true,
|
||||
height: true as const,
|
||||
width: true as const,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+4
-4
@@ -541,10 +541,10 @@ const userSelectToSelectableMap = {
|
||||
};
|
||||
|
||||
const verticalAlignToTextAlignVerticalMap = {
|
||||
auto: 'auto',
|
||||
top: 'top',
|
||||
bottom: 'bottom',
|
||||
middle: 'center',
|
||||
auto: 'auto' as const,
|
||||
top: 'top' as const,
|
||||
bottom: 'bottom' as const,
|
||||
middle: 'center' as const,
|
||||
};
|
||||
|
||||
export default TextImpl;
|
||||
|
||||
+20
-20
@@ -30,23 +30,23 @@ export type NativeTextProps = $ReadOnly<{
|
||||
|
||||
const textViewConfig = {
|
||||
validAttributes: {
|
||||
isHighlighted: true,
|
||||
isPressable: true,
|
||||
numberOfLines: true,
|
||||
ellipsizeMode: true,
|
||||
allowFontScaling: true,
|
||||
dynamicTypeRamp: true,
|
||||
maxFontSizeMultiplier: true,
|
||||
disabled: true,
|
||||
selectable: true,
|
||||
selectionColor: true,
|
||||
adjustsFontSizeToFit: true,
|
||||
minimumFontScale: true,
|
||||
textBreakStrategy: true,
|
||||
onTextLayout: true,
|
||||
dataDetectorType: true,
|
||||
android_hyphenationFrequency: true,
|
||||
lineBreakStrategyIOS: true,
|
||||
isHighlighted: true as const,
|
||||
isPressable: true as const,
|
||||
numberOfLines: true as const,
|
||||
ellipsizeMode: true as const,
|
||||
allowFontScaling: true as const,
|
||||
dynamicTypeRamp: true as const,
|
||||
maxFontSizeMultiplier: true as const,
|
||||
disabled: true as const,
|
||||
selectable: true as const,
|
||||
selectionColor: true as const,
|
||||
adjustsFontSizeToFit: true as const,
|
||||
minimumFontScale: true as const,
|
||||
textBreakStrategy: true as const,
|
||||
onTextLayout: true as const,
|
||||
dataDetectorType: true as const,
|
||||
android_hyphenationFrequency: true as const,
|
||||
lineBreakStrategyIOS: true as const,
|
||||
},
|
||||
directEventTypes: {
|
||||
topTextLayout: {
|
||||
@@ -58,9 +58,9 @@ const textViewConfig = {
|
||||
|
||||
const virtualTextViewConfig = {
|
||||
validAttributes: {
|
||||
isHighlighted: true,
|
||||
isPressable: true,
|
||||
maxFontSizeMultiplier: true,
|
||||
isHighlighted: true as const,
|
||||
isPressable: true as const,
|
||||
maxFontSizeMultiplier: true as const,
|
||||
},
|
||||
uiViewClassName: 'RCTVirtualText',
|
||||
};
|
||||
|
||||
+4
-3
@@ -97,9 +97,10 @@ type HermesEngineSourceType =
|
||||
*/
|
||||
|
||||
const HermesEngineSourceTypes = {
|
||||
LOCAL_PREBUILT_TARBALL: 'local_prebuilt_tarball',
|
||||
DOWNLOAD_PREBUILD_TARBALL: 'download_prebuild_tarball',
|
||||
DOWNLOAD_PREBUILT_NIGHTLY_TARBALL: 'download_prebuilt_nightly_tarball',
|
||||
LOCAL_PREBUILT_TARBALL: 'local_prebuilt_tarball' as const,
|
||||
DOWNLOAD_PREBUILD_TARBALL: 'download_prebuild_tarball' as const,
|
||||
DOWNLOAD_PREBUILT_NIGHTLY_TARBALL:
|
||||
'download_prebuilt_nightly_tarball' as const,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+9
-9
@@ -20,10 +20,10 @@ describe('createCompositeKeyForProps', () => {
|
||||
it('excludes non-array and non-object allowlisted props', () => {
|
||||
const props = {string: 'abc', number: 123, boolean: true, function() {}};
|
||||
const allowlist = {
|
||||
string: true,
|
||||
number: true,
|
||||
boolean: true,
|
||||
function: true,
|
||||
string: true as const,
|
||||
number: true as const,
|
||||
boolean: true as const,
|
||||
function: true as const,
|
||||
};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
|
||||
@@ -53,7 +53,7 @@ describe('createCompositeKeyForProps', () => {
|
||||
bar: new AnimatedEvent([], {useNativeDriver: true}),
|
||||
};
|
||||
const allowlist = {
|
||||
bar: true,
|
||||
bar: true as const,
|
||||
};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('createCompositeKeyForProps', () => {
|
||||
};
|
||||
const allowlist = {
|
||||
style: {
|
||||
baz: true,
|
||||
baz: true as const,
|
||||
},
|
||||
};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
@@ -84,7 +84,7 @@ describe('createCompositeKeyForProps', () => {
|
||||
bar: new AnimatedValue(1),
|
||||
};
|
||||
const allowlist = {
|
||||
bar: true,
|
||||
bar: true as const,
|
||||
};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
|
||||
@@ -108,7 +108,7 @@ describe('createCompositeKeyForProps', () => {
|
||||
const props = {
|
||||
style: {opacity, transform: [{rotateX: 1}, {rotateY}, {rotateZ: 1}]},
|
||||
};
|
||||
const allowlist = {style: {transform: true}};
|
||||
const allowlist = {style: {transform: true as const}};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
|
||||
expect(compositeKey).toEqual({
|
||||
@@ -126,7 +126,7 @@ describe('createCompositeKeyForProps', () => {
|
||||
const props = {
|
||||
style: [{opacity: opacityA}, {opacity: opacityB}],
|
||||
};
|
||||
const allowlist = {style: {opacity: true}};
|
||||
const allowlist = {style: {opacity: true as const}};
|
||||
const compositeKey = createCompositeKeyForProps(props, allowlist);
|
||||
|
||||
expect(compositeKey).toEqual({style: {opacity: opacityB}});
|
||||
|
||||
@@ -240,7 +240,7 @@ const PromptOptions = () => {
|
||||
},
|
||||
{
|
||||
text: 'Custom Cancel',
|
||||
style: 'cancel',
|
||||
style: 'cancel' as const,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -397,7 +397,7 @@ export const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Alert with many Buttons',
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
description: 'It can be used when more than three actions are required.',
|
||||
render(): React.Node {
|
||||
return <AlertWithManyButtons />;
|
||||
@@ -405,7 +405,7 @@ export const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Alert with cancelable={true}',
|
||||
platform: 'android',
|
||||
platform: 'android' as const,
|
||||
description:
|
||||
'By passing cancelable={false} prop to alerts on Android, they can be dismissed by tapping outside of the alert box.',
|
||||
render(): React.Node {
|
||||
@@ -414,7 +414,7 @@ export const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Alert with styles',
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
description:
|
||||
"Alert buttons can be styled. There are three button styles - 'default' | 'cancel' | 'destructive'.",
|
||||
render(): React.Node {
|
||||
@@ -423,7 +423,7 @@ export const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Alert with styles + preferred',
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
description:
|
||||
"Alert buttons with 'isPreferred' will be emphasized, even over cancel buttons",
|
||||
render(): React.Node {
|
||||
@@ -432,14 +432,14 @@ export const examples = [
|
||||
},
|
||||
{
|
||||
title: 'Prompt Options',
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
render(): React.Node {
|
||||
return <PromptOptions />;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Prompt Types',
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
render(): React.Node {
|
||||
return <PromptTypes />;
|
||||
},
|
||||
|
||||
@@ -47,7 +47,7 @@ type CircleBlockProps = $ReadOnly<{
|
||||
|
||||
function CircleBlock({children, style}: CircleBlockProps): React.Node {
|
||||
const circleStyle = {
|
||||
flexDirection: 'row',
|
||||
flexDirection: 'row' as const,
|
||||
backgroundColor: '#f6f7f8',
|
||||
borderWidth: 0.5,
|
||||
borderColor: '#d6d7da',
|
||||
|
||||
@@ -428,7 +428,7 @@ const examples = [
|
||||
title: 'Pressable with Ripple and Animated child',
|
||||
description:
|
||||
('Pressable can have an AnimatedComponent as a direct child.': string),
|
||||
platform: 'android',
|
||||
platform: 'android' as const,
|
||||
render: function (): React.Node {
|
||||
const mScale = new Animated.Value(1);
|
||||
Animated.timing(mScale, {
|
||||
@@ -455,10 +455,10 @@ const examples = [
|
||||
title: 'Pressable with custom Ripple',
|
||||
description:
|
||||
("Pressable can specify ripple's radius, color and borderless params": string),
|
||||
platform: 'android',
|
||||
platform: 'android' as const,
|
||||
render: function (): React.Node {
|
||||
const nativeFeedbackButton = {
|
||||
textAlign: 'center',
|
||||
textAlign: 'center' as const,
|
||||
margin: 10,
|
||||
};
|
||||
return (
|
||||
@@ -543,7 +543,7 @@ const examples = [
|
||||
render: function (): React.Node {
|
||||
return <ForceTouchExample />;
|
||||
},
|
||||
platform: 'ios',
|
||||
platform: 'ios' as const,
|
||||
},
|
||||
{
|
||||
title: 'Pressable Hit Slop',
|
||||
|
||||
@@ -1117,7 +1117,11 @@ function TextBaseLineLayoutExample(props: {}): React.Node {
|
||||
const marker = (
|
||||
<View style={{width: 20, height: 20, backgroundColor: 'gray'}} />
|
||||
);
|
||||
const subtitleStyle = {fontSize: 16, marginTop: 8, fontWeight: 'bold'};
|
||||
const subtitleStyle = {
|
||||
fontSize: 16,
|
||||
marginTop: 8,
|
||||
fontWeight: 'bold' as const,
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
|
||||
@@ -296,7 +296,11 @@ class TextBaseLineLayoutExample extends React.Component<{}, mixed> {
|
||||
const marker = (
|
||||
<View style={{width: 20, height: 20, backgroundColor: 'gray'}} />
|
||||
);
|
||||
const subtitleStyle = {fontSize: 16, marginTop: 8, fontWeight: 'bold'};
|
||||
const subtitleStyle = {
|
||||
fontSize: 16,
|
||||
marginTop: 8,
|
||||
fontWeight: 'bold' as const,
|
||||
};
|
||||
|
||||
return (
|
||||
<View>
|
||||
@@ -1475,7 +1479,7 @@ const examples = [
|
||||
{
|
||||
title: 'Dynamic Type (iOS only)',
|
||||
render: function (): React.Node {
|
||||
const boldStyle = {fontWeight: 'bold'};
|
||||
const boldStyle = {fontWeight: 'bold' as const};
|
||||
const boxStyle = {
|
||||
borderWidth: 1,
|
||||
padding: 8,
|
||||
|
||||
@@ -19,8 +19,8 @@ import type {
|
||||
import RNTesterList from './RNTesterList';
|
||||
|
||||
export const Screens = {
|
||||
COMPONENTS: 'components',
|
||||
APIS: 'apis',
|
||||
COMPONENTS: 'components' as const,
|
||||
APIS: 'apis' as const,
|
||||
PLAYGROUNDS: 'playgrounds',
|
||||
};
|
||||
|
||||
@@ -106,14 +106,14 @@ export const getExamplesListWithRecentlyUsed = ({
|
||||
],
|
||||
[Screens.APIS]: [
|
||||
{
|
||||
key: 'RECENT_APIS',
|
||||
key: 'RECENT_APIS' as const,
|
||||
data: recentlyUsedAPIs,
|
||||
title: 'Recently viewed',
|
||||
title: 'Recently viewed' as const,
|
||||
},
|
||||
{
|
||||
key: 'APIS',
|
||||
key: 'APIS' as const,
|
||||
data: apis.sort((a, b) => a.module.title.localeCompare(b.module.title)),
|
||||
title: 'APIs',
|
||||
title: 'APIs' as const,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user