Calling for help from TypeScript experts, I'm running into an error while trying to rewrite a React Higher Order Component (HOC) into TS. I'm not sure how to go about solving this.
"src/withEnv.tsx(15,14): error TS4025: Exported variable 'withEnv' has or is using private name 'WithEnv'."
My code -
import * as React from 'react'
import Context from './context'
import hoistNonReactStatic from 'hoist-non-react-statics'
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
function defaultEnvToProps(context) {
return {
env: context,
}
}
const withEnv = (mapper) => (Component) => {
class WithEnv extends React.Component {
static displayName: string
render() {
return (
<Context.Consumer>
{(context) => {
const props = Object.assign(
{},
typeof mapper === 'function'
? mapper(context)
: defaultEnvToProps(context),
this.props
)
return <Component {...props} />
}}
</Context.Consumer>
)
}
}
WithEnv.displayName = WithEnv(${getDisplayName(Component)})
hoistNonReactStatic(WithEnv, Component)
return WithEnv
}
export default withEnv
There's a lot of information that goes into typing this properly. Some of that info, such as the type of the context value, is missing from your question so I have tried to fill in the blanks.
I can explain the primary error that you are concerned about and tell you how to fix it.
"src/withEnv.tsx(15,14): error TS4025: Exported variable 'withEnv' has or is using private name 'WithEnv'."
There is a discussion on GitHub which will tell you more about this issue. Typescript chokes because it wants to describe the return type of your withEnv
HOC as the WithEnv
private class. But the declarations file won't include this class because it only exists inside your function, so it cannot be used as a return type. In order to avoid the error you can manually type the return type of withEnv
as some component which takes a particular set of props.
import * as React from 'react'
import { ComponentType } from 'react';
import hoistNonReactStatic from 'hoist-non-react-statics'
type ContextValue = {} // what is this ??????
declare const Context: React.Context<ContextValue>;
function getDisplayName(WrappedComponent: ComponentType<any>): string {
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
function defaultEnvToProps(context: ContextValue): { env: ContextValue } {
return {
env: context,
}
}
// define a type for a function that maps your context to props
type Mapper<Return> = (context: ContextValue) => Return;
// generic type `MappedProps` describes the props returned by the mapper
// default value for `MappedProps` type matches `defaultEnvToProps`
export const withEnv = <MappedProps extends {} = { env: ContextValue }>(
// mapper is an optional function that maps from the context value to the added props
mapper?: Mapper<MappedProps>
) =>
// returned function is generic depending on the component props
<Props extends {}>(
// takes a component which uses the added props
Component: ComponentType<MappedProps & Omit<Props, keyof MappedProps>>
// returns a component which doesn't need those props
): ComponentType<Omit<Props, keyof MappedProps>> => {
// you could just as well use a function component here and use `useContext`
class WithEnv extends React.Component<Omit<Props, keyof MappedProps>> {
static displayName: string;
render() {
return (
<Context.Consumer>
{(context) => {
const mappedProps = mapper ? mapper(context) : defaultEnvToProps(context);
return (
<Component
{...this.props}
{...mappedProps as MappedProps} // this assertion is only necessary is `mapper` is optional
/>
)
}}
</Context.Consumer>
)
}
}
WithEnv.displayName = `WithEnv(${getDisplayName(Component)})`;
hoistNonReactStatic(WithEnv, Component);
return WithEnv;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.