Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'TextInputProps'

It is HOC, I have follow errors:

Type 'Pick, "children" | Exclude> & { ...; }' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'

Type 'Pick, "children" | Exclude> & { ...; }' is not assignable to type 'P'.

'Pick, "children" | Exclude> & { ...; }' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'TextInputProps'.

How repair it?

import { View, ViewStyle, StyleProp, NativeSyntheticEvent, TextStyle, TextInputProps } from 'react-native';

type Props = {
  styleWrapper?: StyleProp<ViewStyle>;
  style?: StyleProp<TextStyle>;
  title?: string;
  value?: string;
  onFocus?: (e: NativeSyntheticEvent<any>) => void;
  onBlur?: (e: NativeSyntheticEvent<any>) => void;
  onChange?: (text: string) => void;
  onReset?: () => void;
  withReset?: boolean;
};

export const withInputWrapper = <P extends TextInputProps = TextInputProps>(
  InputComponent: React.ComponentType<P>
): React.FC<P & Props> => {
  return ({ styleWrapper, style, title, value, onFocus, onBlur, onChange, onReset, withReset = true, ...props }) => {
    const onFocusHandler = ...
    const onBlurHandler = ...

    const onChangeHandler = ...

    return (
      <View style={styleWrapper}>
        <View style={s.inputWrapper}>
          <InputComponent // <-- here
            {...props}
            onChange={onChangeHandler}
            value={value}
            style={style}
            onBlur={onBlurHandler}
            onFocus={onFocusHandler}
          />
        </View>
      </View>
    );
  };
};
like image 600
Igor Zvyagin Avatar asked Mar 18 '20 07:03

Igor Zvyagin


2 Answers

This is one of those cases when you have reached the limit of what TS can reason about generic type parameters with conditional and mapped types. While props might seem obviously P, this is not obvious to the compiler. The compiler will use Omit to type props which uses mapped and conditional types to remove some keys from a given type. So props will be typed as Omit<P & Props, keyof Props>. This might seem obviously P, but TS can't follow conditional types as long as they still have unresolved type parameters. This means, as far as TS is concerned Omit<P & Props, keyof Props> and P are different types.

The only solution here is to use a type assertion:

export const withInputWrapper = <P extends TextInputProps = TextInputProps>(
  InputComponent: React.ComponentType<P>
): React.FC<P & Props> => {
  return ({ styleWrapper, style, title, value, onFocus, onBlur, onChange, onReset, withReset = true, ...props }) => {

    const onFocusHandler = () => {}
    const onBlurHandler = () => {}

      const onChangeHandler = () => {}

    return (
      <View style={styleWrapper}>
        <View>
          <InputComponent // <-- here
            {...props as P}
            onChange={onChangeHandler}
            value={value}
            style={style}
            onBlur={onBlurHandler}
            onFocus={onFocusHandler}
          />
        </View>
      </View>
    );
  };
};

Playground Link

like image 171
Titian Cernicova-Dragomir Avatar answered Oct 04 '22 22:10

Titian Cernicova-Dragomir


I have to quote a different stackoverflow answer explaining your problem: could be instantiated with a different subtype of constraint 'object'

Basically TS cannot infer which subtype you are using and doesn't let you assign/assume a concrete type directly.

Here's a sandbox I created which fixes a similar issue by not using generics (it's not react-native but shouldn't matter in this case): https://codesandbox.io/s/serverless-tdd-91zfq

type Props = {
  styleWrapper?: any;
  style?: any;
  title?: string;
  value?: string;
  onFocus?: (e: any) => void;
  onBlur?: (e: any) => void;
  onChange?: (text: string) => void;
  onReset?: () => void;
  withReset?: boolean;
} & React.InputHTMLAttributes<HTMLInputElement>;

export const withInputWrapper = (
  InputComponent: React.ComponentType<Props>
) => ({
...

If you want to use generics you need to cast ...props to P: {...props as P} inside return.

like image 35
tudor.gergely Avatar answered Oct 04 '22 23:10

tudor.gergely