Skip to content

Commit 76c1aaa

Browse files
NickGerlemanfacebook-github-bot
authored andcommitted
Enforce compatibility with exactOptionalPropertyTypes
Summary: `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 DefinitelyTyped/DefinitelyTyped@694c663). We have not always followed this (I have myself previously assumed the two are equivalent), so this sets the flag in our tsconfig, and updates properties which are not compatible right now. 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` Differential Revision: D43700862 fbshipit-source-id: 28682e82caac8b6f0f177fe6bd0039cbf21e3c59
1 parent a49446b commit 76c1aaa

File tree

27 files changed

+147
-81
lines changed

27 files changed

+147
-81
lines changed

.eslintrc.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,17 @@ module.exports = {
8282
{
8383
files: ['**/*.{ts,tsx}'],
8484
parser: '@typescript-eslint/parser',
85-
plugins: ['@typescript-eslint/eslint-plugin'],
85+
plugins: ['@typescript-eslint/eslint-plugin', 'redundant-undefined'],
8686
rules: {
8787
'@typescript-eslint/no-unused-vars': 'off',
8888
'react-native/no-inline-styles': 'off',
8989
'@typescript-eslint/no-shadow': 'off',
9090
'no-self-compare': 'off',
9191
'react/self-closing-comp': 'off',
92+
'redundant-undefined/redundant-undefined': [
93+
'error',
94+
{followExactOptionalPropertyTypes: true},
95+
],
9296
},
9397
},
9498
],

Libraries/Alert/Alert.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
export interface AlertButton {
1414
text?: string | undefined;
1515
onPress?: ((value?: string) => void) | undefined;
16-
isPreferred?: boolean;
16+
isPreferred?: boolean | undefined;
1717
style?: 'default' | 'cancel' | 'destructive' | undefined;
1818
}
1919

2020
interface AlertOptions {
2121
/** @platform android */
2222
cancelable?: boolean | undefined;
23-
userInterfaceStyle?: 'unspecified' | 'light' | 'dark';
23+
userInterfaceStyle?: 'unspecified' | 'light' | 'dark' | undefined;
2424
/** @platform android */
2525
onDismiss?: (() => void) | undefined;
2626
}

Libraries/Animated/Animated.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,7 +579,7 @@ export namespace Animated {
579579
extends React.FC<AnimatedProps<React.ComponentPropsWithRef<T>>> {}
580580

581581
export type AnimatedComponentOptions = {
582-
collapsable?: boolean;
582+
collapsable?: boolean | undefined;
583583
};
584584

585585
/**

Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export interface AccessibilityInfoStatic {
137137
*/
138138
announceForAccessibilityWithOptions(
139139
announcement: string,
140-
options: {queue?: boolean},
140+
options: {queue?: boolean | undefined},
141141
): void;
142142

143143
/**

Libraries/Components/Pressable/Pressable.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ export interface PressableProps
158158
/**
159159
* Duration (in milliseconds) to wait after press down before calling onPressIn.
160160
*/
161-
unstable_pressDelay?: number;
161+
unstable_pressDelay?: number | undefined;
162162
}
163163

164164
// TODO use React.AbstractComponent when available

Libraries/Components/ScrollView/ScrollView.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ export interface ScrollViewProps
730730
/**
731731
* When true, Sticky header is hidden when scrolling down, and dock at the top when scrolling up.
732732
*/
733-
stickyHeaderHiddenOnScroll?: boolean;
733+
stickyHeaderHiddenOnScroll?: boolean | undefined;
734734

735735
/**
736736
* Style
@@ -841,7 +841,7 @@ export class ScrollView extends ScrollViewBase {
841841
* The options object has an animated prop, that enables the scrolling animation or not.
842842
* The animated prop defaults to true
843843
*/
844-
scrollToEnd(options?: {animated?: boolean}): void;
844+
scrollToEnd(options?: {animated?: boolean | undefined}): void;
845845

846846
/**
847847
* Displays the scroll indicators momentarily.

Libraries/Components/TextInput/InputAccessoryView.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export class InputAccessoryView extends React.Component<InputAccessoryViewProps>
2323
export interface InputAccessoryViewProps {
2424
backgroundColor?: ColorValue | undefined;
2525

26-
children?: React.ReactNode;
26+
children?: React.ReactNode | undefined;
2727

2828
/**
2929
* An ID which is used to associate this InputAccessoryView to specified TextInput(s).

Libraries/Components/Touchable/TouchableWithoutFeedback.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface TouchableWithoutFeedbackProps
4040
extends TouchableWithoutFeedbackPropsIOS,
4141
TouchableWithoutFeedbackPropsAndroid,
4242
AccessibilityProps {
43-
children?: React.ReactNode;
43+
children?: React.ReactNode | undefined;
4444

4545
/**
4646
* Delay in ms, from onPressIn, before onLongPress is called.

Libraries/Components/View/ViewAccessibility.d.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@ export interface AccessibilityProps
7575
*/
7676
accessibilityValue?: AccessibilityValue | undefined;
7777

78-
'aria-valuemax'?: AccessibilityValue['max'];
79-
'aria-valuemin'?: AccessibilityValue['min'];
80-
'aria-valuenow'?: AccessibilityValue['now'];
81-
'aria-valuetext'?: AccessibilityValue['text'];
78+
'aria-valuemax'?: AccessibilityValue['max'] | undefined;
79+
'aria-valuemin'?: AccessibilityValue['min'] | undefined;
80+
'aria-valuenow'?: AccessibilityValue['now'] | undefined;
81+
'aria-valuetext'?: AccessibilityValue['text'] | undefined;
8282
/**
8383
* When `accessible` is true, the system will try to invoke this function when the user performs an accessibility custom action.
8484
*/
@@ -105,7 +105,7 @@ export interface AccessibilityProps
105105
/**
106106
* Indicates to accessibility services to treat UI component like a specific role.
107107
*/
108-
role?: Role;
108+
role?: Role | undefined;
109109
}
110110

111111
export type AccessibilityActionInfo = Readonly<{

Libraries/Components/View/ViewPropTypes.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ export interface ViewProps
173173
Touchable,
174174
PointerEvents,
175175
AccessibilityProps {
176-
children?: React.ReactNode;
176+
children?: React.ReactNode | undefined;
177177
/**
178178
* This defines how far a touch event can start away from the view.
179179
* Typical interface guidelines recommend touch targets that are at least

Libraries/Image/Image.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ export interface ImagePropsBase
290290
*
291291
* See https://reactnative.dev/docs/image#crossorigin
292292
*/
293-
crossOrigin?: 'anonymous' | 'use-credentials';
293+
crossOrigin?: 'anonymous' | 'use-credentials' | undefined;
294294

295295
/**
296296
* Changes the color of all the non-transparent pixels to the tintColor.
@@ -359,7 +359,7 @@ export class Image extends ImageBase {
359359
}
360360

361361
export interface ImageBackgroundProps extends ImagePropsBase {
362-
children?: React.ReactNode;
362+
children?: React.ReactNode | undefined;
363363
imageStyle?: StyleProp<ImageStyle> | undefined;
364364
style?: StyleProp<ViewStyle> | undefined;
365365
imageRef?(image: Image): void;

Libraries/Lists/FlatList.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export interface FlatListProps<ItemT> extends VirtualizedListProps<ItemT> {
5050
* If any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the `data` prop,
5151
* stick it here and treat it immutably.
5252
*/
53-
extraData?: any;
53+
extraData?: any | undefined;
5454

5555
/**
5656
* `getItemLayout` is an optional optimization that lets us skip measurement of dynamic
@@ -144,7 +144,7 @@ export interface FlatListProps<ItemT> extends VirtualizedListProps<ItemT> {
144144
/**
145145
* See `ViewabilityHelper` for flow type and further documentation.
146146
*/
147-
viewabilityConfig?: any;
147+
viewabilityConfig?: any | undefined;
148148

149149
/**
150150
* Note: may have bugs (missing content) in some circumstances - use at your own risk.

Libraries/Lists/SectionList.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export interface SectionListProps<ItemT, SectionT = DefaultSectionT>
7575
* If any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the `data` prop,
7676
* stick it here and treat it immutably.
7777
*/
78-
extraData?: any;
78+
extraData?: any | undefined;
7979

8080
/**
8181
* `getItemLayout` is an optional optimization that lets us skip measurement of dynamic

Libraries/Text/Text.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export interface TextProps
107107
*/
108108
allowFontScaling?: boolean | undefined;
109109

110-
children?: React.ReactNode;
110+
children?: React.ReactNode | undefined;
111111

112112
/**
113113
* This can be one of the following values:

Libraries/Utilities/createPerformanceLogger.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
export type Timespan = {
1111
startTime: number;
12-
endTime?: number;
13-
totalTime?: number;
14-
startExtras?: Extras;
15-
endExtras?: Extras;
12+
endTime?: number | undefined;
13+
totalTime?: number | undefined;
14+
startExtras?: Extras | undefined;
15+
endExtras?: Extras | undefined;
1616
};
1717

1818
// Extra values should be serializable primitives

packages/react-native-codegen/src/CodegenSchema.d.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export interface VoidTypeAnnotation {
4747
export interface ObjectTypeAnnotation<T> {
4848
readonly type: 'ObjectTypeAnnotation';
4949
readonly properties: readonly NamedShape<T>[];
50-
readonly baseTypes?: readonly string[];
50+
readonly baseTypes?: readonly string[] | undefined;
5151
}
5252

5353
export interface FunctionTypeAnnotation<P, R> {
@@ -77,10 +77,10 @@ export interface ComponentShape extends OptionsShape {
7777
}
7878

7979
export interface OptionsShape {
80-
readonly interfaceOnly?: boolean;
81-
readonly paperComponentName?: string;
82-
readonly excludedPlatforms?: readonly PlatformType[];
83-
readonly paperComponentNameDeprecated?: string;
80+
readonly interfaceOnly?: boolean | undefined;
81+
readonly paperComponentName?: string | undefined;
82+
readonly excludedPlatforms?: readonly PlatformType[] | undefined;
83+
readonly paperComponentNameDeprecated?: string | undefined;
8484
}
8585

8686
export interface ExtendsPropsShape {
@@ -94,10 +94,10 @@ export interface EventTypeShape {
9494
| 'direct'
9595
| 'bubble';
9696
readonly optional: boolean;
97-
readonly paperTopLevelNameDeprecated?: string;
97+
readonly paperTopLevelNameDeprecated?: string | undefined;
9898
readonly typeAnnotation: {
9999
readonly type: 'EventTypeAnnotation';
100-
readonly argument?: ObjectTypeAnnotation<EventTypeAnnotation>;
100+
readonly argument?: ObjectTypeAnnotation<EventTypeAnnotation> | undefined;
101101
};
102102
}
103103

@@ -211,7 +211,7 @@ export interface NativeModuleSchema {
211211
readonly enumMap: NativeModuleEnumMap;
212212
readonly spec: NativeModuleSpec;
213213
readonly moduleName: string;
214-
readonly excludedPlatforms?: readonly PlatformType[];
214+
readonly excludedPlatforms?: readonly PlatformType[] | undefined;
215215
}
216216

217217
export interface NativeModuleSpec {
@@ -234,7 +234,7 @@ export type NativeModuleObjectTypeAnnotation = ObjectTypeAnnotation<Nullable<Nat
234234

235235
export interface NativeModuleArrayTypeAnnotation<T extends Nullable<NativeModuleBaseTypeAnnotation>> {
236236
readonly type: 'ArrayTypeAnnotation';
237-
readonly elementType?: T;
237+
readonly elementType?: T | undefined;
238238
}
239239

240240
export interface NativeModuleStringTypeAnnotation {
@@ -294,7 +294,7 @@ export interface NativeModuleTypeAliasTypeAnnotation {
294294

295295
export interface NativeModulePromiseTypeAnnotation {
296296
readonly type: 'PromiseTypeAnnotation';
297-
readonly elementType?: Nullable<NativeModuleBaseTypeAnnotation>;
297+
readonly elementType?: Nullable<NativeModuleBaseTypeAnnotation> | undefined;
298298
}
299299

300300
export type UnionTypeAnnotationMemberType =

packages/react-native-codegen/src/SchemaValidator.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import type { SchemaType } from "./CodegenSchema";
8+
import type { SchemaType } from './CodegenSchema';
99

1010
export declare function getErrors(schema: SchemaType): readonly string[];
1111
export declare function validate(schema: SchemaType): void;

packages/react-native-codegen/src/parsers/parser.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import type { SchemaType } from "../CodegenSchema";
9-
import type { ParserType } from "./errors";
8+
import type { SchemaType } from '../CodegenSchema';
9+
import type { ParserType } from './errors';
1010

1111
// useful members only for downstream
1212
export interface Parser {

packages/react-native-codegen/src/parsers/schema/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
* LICENSE file in the root directory of this source tree.
66
*/
77

8-
import type { SchemaType } from "../../CodegenSchema";
8+
import type { SchemaType } from '../../CodegenSchema';
99

1010
export declare function parse(filename: string): SchemaType | undefined;

packages/rn-tester/js/examples/SectionList/SectionList-scrollable.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,10 @@ export function SectionList_scrollable(Props: {
281281
ref={ref}
282282
ListHeaderComponent={HeaderComponent}
283283
ListFooterComponent={FooterComponent}
284-
// eslint-disable-next-line react/no-unstable-nested-components
285284
// $FlowFixMe[missing-local-annot]
286285
SectionSeparatorComponent={info => (
287286
<CustomSeparatorComponent {...info} text="SECTION SEPARATOR" />
288287
)}
289-
// eslint-disable-next-line react/no-unstable-nested-components
290288
// $FlowFixMe[missing-local-annot]
291289
ItemSeparatorComponent={info => (
292290
<CustomSeparatorComponent {...info} text="ITEM SEPARATOR" />

packages/virtualized-lists/Lists/VirtualizedList.d.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export interface ViewToken {
2323
key: string;
2424
index: number | null;
2525
isViewable: boolean;
26-
section?: any;
26+
section?: any | undefined;
2727
}
2828

2929
export interface ViewabilityConfig {
@@ -188,7 +188,7 @@ export interface VirtualizedListWithoutRenderItemProps<ItemT>
188188
* The default accessor functions assume this is an Array<{key: string}> but you can override
189189
* getItem, getItemCount, and keyExtractor to handle any type of index-based data.
190190
*/
191-
data?: any;
191+
data?: any | undefined;
192192

193193
/**
194194
* `debug` will turn on extra logging and visual overlays to aid with debugging both usage and
@@ -208,7 +208,7 @@ export interface VirtualizedListWithoutRenderItemProps<ItemT>
208208
* any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the
209209
* `data` prop, stick it here and treat it immutably.
210210
*/
211-
extraData?: any;
211+
extraData?: any | undefined;
212212

213213
/**
214214
* A generic accessor for extracting an item from any sort of data blob.

repo-config/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"eslint-plugin-react": "^7.30.1",
3838
"eslint-plugin-react-hooks": "^4.6.0",
3939
"eslint-plugin-react-native": "^4.0.0",
40+
"eslint-plugin-redundant-undefined": "^0.4.0",
4041
"eslint-plugin-relay": "^1.8.3",
4142
"flow-bin": "^0.200.0",
4243
"inquirer": "^7.1.0",

types/__typetests__/fabric-component-sample.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,16 @@ type Event = Readonly<{
2727
}>;
2828

2929
interface NativeProps extends ViewProps {
30-
string?: string;
31-
number?: number;
32-
boolean?: boolean;
33-
default?: WithDefault<'option1' | 'option2', 'option1'>;
34-
double?: Double;
35-
float?: Float;
36-
int32?: Int32;
37-
unsafeObject?: UnsafeObject;
38-
onBubblingEventHandler?: BubblingEventHandler<Event>;
39-
onDirectEventHandler?: DirectEventHandler<Event>;
30+
string?: string | undefined;
31+
number?: number | undefined;
32+
boolean?: boolean | undefined;
33+
default?: WithDefault<'option1' | 'option2', 'option1'> | undefined;
34+
double?: Double | undefined;
35+
float?: Float | undefined;
36+
int32?: Int32 | undefined;
37+
unsafeObject?: UnsafeObject | undefined;
38+
onBubblingEventHandler?: BubblingEventHandler<Event> | undefined;
39+
onDirectEventHandler?: DirectEventHandler<Event> | undefined;
4040
}
4141

4242
export type SampleViewType = NativeComponentType<NativeProps>;

types/modules/Codegen.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@ declare module 'react-native/Libraries/Utilities/codegenNativeComponent' {
2323
import type {HostComponent} from 'react-native';
2424

2525
export interface Options {
26-
readonly interfaceOnly?: boolean;
27-
readonly paperComponentName?: string;
28-
readonly paperComponentNameDeprecated?: string;
29-
readonly excludedPlatforms?: ReadonlyArray<'iOS' | 'android'>;
26+
readonly interfaceOnly?: boolean | undefined;
27+
readonly paperComponentName?: string | undefined;
28+
readonly paperComponentNameDeprecated?: string | undefined;
29+
readonly excludedPlatforms?: ReadonlyArray<'iOS' | 'android'> | undefined;
3030
}
3131

3232
export type NativeComponentType<T> = HostComponent<T>;

0 commit comments

Comments
 (0)