Ethiraj Sukumar
Ethiraj Sukumar

Reputation: 23

ts: 'Props' could be instantiated with an arbitrary type which could be unrelated to another type

const withFirebase = <Props extends {firebase: Firebase}>(
  Component: React.ComponentType<Props>
): ComponentType<Omit<Props, "firebase">> => (props) => (
  <FirebaseContext.Consumer>
    {(firebase) => <Component {...props} firebase={firebase} />}
  </FirebaseContext.Consumer>
);

export default withFirebase;

Here

<Component {...props} firebase={firebase} />

Throws the below typescript error

Type 'Pick<Props, Exclude<keyof Props, "firebase">> & { firebase: Firebase | null; children?: ReactNode; }' is not assignable to type 'IntrinsicAttributes & Props & { children?: ReactNode; }'.
  Type 'Pick<Props, Exclude<keyof Props, "firebase">> & { firebase: Firebase | null; children?: ReactNode; }' is not assignable to type 'Props'.
    'Props' could be instantiated with an arbitrary type which could be unrelated to 'Pick<Props, Exclude<keyof Props, "firebase">> & { firebase: Firebase | null; children?: ReactNode; }

Omit<Props, "firebase"> is added to avoid the required prop error in the Wrapped component import.

like

const App = () => {
  return (
    <ItemWithFirebase /> // If not omit is available this will throw prop firebase not available error
  )
}

const Item: FC<{firebase: Firebase}> = ({firebase}) => {
...
}

const ItemWithFirebase = withFirebase(Item);

I have casted the spread props as below

<Component {...(props as Props)} firebase={firebase} />}

But I am not sure if I am doing it right. Could you please let me know if there are any other way to tackle this error?

Upvotes: 2

Views: 642

Answers (1)

Linda Paiste
Linda Paiste

Reputation: 42188

This comes up all the time when writing HOCs. You're looking at the error and it says "We take Props, we remove Props['firebase'], we add {firebase: Firebase | null}, and we're not sure if this object is assignable to Props". You might think, "if I removed firebase and then added it back, of course that's the same thing". Technically, Props extends {firebase: Firebase} means that Props['firebase'] could be something more specific than Firebase, in which case the value that you provide to it wouldn't be sufficient. That weird edge case is one of two reasons that you get an error.

The other reason is that Props doesn't allow for firebase to be null, but the Consumer says that it could either have Firebase or null.

But 99.9% of the time it is the same thing, so it's fine to assert {...(props as Props)} like you have done here. You might want to expand Props to allow null, but if you know that it's not going to be null in your app then it's not an issue.

You can avoid some of the errors if you let your generic Props represent the props without firebase. Instead of dropping firebase from the return, we add it to the Component. We Omit any existing definition of firebase from Props which rules out the edge case where the Component requires some value of firebase other than what we provide.

const withFirebase = <Props extends {}>(
  Component: React.ComponentType<Omit<Props, 'firebase'> & {firebase: Firebase | null}>
): ComponentType<Props> => (props) => (
  <FirebaseContext.Consumer>
    {(firebase) => <Component {...props} firebase={firebase} />}
  </FirebaseContext.Consumer>
);

Upvotes: 1

Related Questions