From 1ac3032b21f98592663549c1c27d70efcdff9dfd Mon Sep 17 00:00:00 2001 From: Max Davidson Date: Thu, 27 Sep 2018 13:09:49 +0200 Subject: [PATCH] Improve HOC type inference --- types/react-navigation/index.d.ts | 36 ++++++++++++++++--- .../react-navigation-tests.tsx | 32 +++++++++++------ 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/types/react-navigation/index.d.ts b/types/react-navigation/index.d.ts index e671e6c9ac..80b54d23a2 100644 --- a/types/react-navigation/index.d.ts +++ b/types/react-navigation/index.d.ts @@ -24,6 +24,7 @@ // Gustavo Brunoro // Denis Frezzato // Mickael Wegerich +// Max Davidson // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 @@ -1234,20 +1235,45 @@ export class Header extends React.Component { static HEIGHT: number; } +export type Omit = Pick>; + +export type InferProps> = T extends React.ComponentType ? P : never; + export interface NavigationInjectedProps

{ navigation: NavigationScreenProp; } +// If the wrapped component is a class, we can get a ref to it +export function withNavigation>( + Component: T, +): React.ComponentType, keyof NavigationInjectedProps> & { onRef?: React.Ref> }>; + +export function withNavigation>( + Component: T, +): React.ComponentType, keyof NavigationInjectedProps>>; + +// For backwards compatibility export function withNavigation( - Component: React.ComponentType> + Component: React.ComponentType>, ): React.ComponentType>> }>; -export interface NavigationFocusInjectedProps extends NavigationInjectedProps { +export interface NavigationFocusInjectedProps

extends NavigationInjectedProps

{ isFocused: boolean; } -export function withNavigationFocus( - Component: React.ComponentType -): React.ComponentType }>; + +// If the wrapped component is a class, we can get a ref to it +export function withNavigationFocus>( + Component: T, +): React.ComponentType, keyof NavigationFocusInjectedProps> & { onRef?: React.Ref> }>; + +export function withNavigationFocus>( + Component: T, +): React.ComponentType, keyof NavigationFocusInjectedProps>>; + +// For backwards compatibility +export function withNavigationFocus( + Component: React.ComponentType>, +): React.ComponentType>> }>; /** * SafeAreaView Component diff --git a/types/react-navigation/react-navigation-tests.tsx b/types/react-navigation/react-navigation-tests.tsx index a9063577a6..47dd7baa0e 100644 --- a/types/react-navigation/react-navigation-tests.tsx +++ b/types/react-navigation/react-navigation-tests.tsx @@ -550,20 +550,32 @@ class MyBackButton extends React.Component(MyBackButton); +// withNavigation returns a component that wraps MyBackButton and passes in the navigation prop. +// If you have class methods, you should have a way to use them. +const BackButtonWithNavigation = withNavigation(MyBackButton); const BackButtonInstance = { const backButtonRef = ref; }} + title="Back" onRef={ref => { + // ref is inferred as MyBackButton | null + if (!ref) return; + ref.triggerBack(); + }} />; -// if you have class methods, you should have a way to use them -const BackButtonWithNavigationSpecified = withNavigation(MyBackButton); -const BackButtonSpecifiedInstance = ; +} + +// Wrapped stateless components don't accept an onRef +const StatelessBackButtonWithNavigation = withNavigation(StatelessBackButton); +const StatelessBackButtonInstance = ; + +// The old way of passing in the props should still work +const BackButtonWithNavigationWithExplicitProps = withNavigation(MyBackButton); +const BackButtonWithExplicitPropsInstance = { if (!ref) return; - const backButtonRef = ref as MyBackButton; - backButtonRef.triggerBack(); + // We can't infer the component type if we pass in the props + (ref as MyBackButton).triggerBack(); }} />; @@ -578,7 +590,7 @@ class MyFocusedComponent extends React.Component(MyFocusedComponent); +const MyFocusedComponentWithNavigationFocus = withNavigationFocus(MyFocusedComponent); const MyFocusedComponentInstance = { const backButtonRef = ref; }} />;