Error thrown when verifying the type of a React Higher Order Component's nested component

When utilizing the React HOC function, the component parameter fails type checking with the following code snippet:

import * as React from 'react';

interface FooProps {
  foo: string;
  bar: string;
}

function withProps<P extends FooProps>(
  Comp: React.ComponentType<P>
) {
  return class extends React.Component {
    render() {
      return <Comp foo="foo" bar="bar"/>  // "Comp" fail typecheck
    }
  }
}

I believed that the constraint P extends FooProps would be sufficient, but unfortunately it is not.

This error was encountered:

Type '{ foo: string; bar: string; }' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'.
  Property 'foo' does not exist on type 'IntrinsicAttributes & P & { children?: ReactNode; }'.

Interestingly, Visual Studio Code's intellisense correctly identified foo and bar props as FooProps.foo and FooProps.bar respectively, but stumbled on Comp.

Why doesn't this function work as expected?

It seems that there might be a misunderstanding in how generic functions infer variable types.

NOTE: Utilizing

Comp: React.ComponentType<FooProps>
resolves the issue successfully.

Answer №1

Sure thing.

My issue with Typescript arises from the fact that I am only selecting declared props within Props, such as foo and bar, even though extends implies that type P could potentially have more props.

To address this, I figured out a solution by passing any additional, unknown props from P using ...this.props. It looks something like this:

import * as React from 'react';

interface FooProps {
  foo: string;
  bar: string;
}

function withProps<P extends FooProps>(
  Comp: React.ComponentType<P>
) {
  return class extends React.Component<P> {
    render() {
      return <Comp {...this.props} foo="foo" bar="bar" />
    }
  }
}

Notice how ...this.props is used in <Comp .../> to ensure that the necessary P props are also passed to the Higher Order Component.

Answer №2

Let me revise my answer once again.

The previous response is inaccurate as ...this.props overrides the required props and their types. By adjusting this, the typecheck should not fail as it currently does.

    render() {
      return <Comp {...this.props} foo1="foo" bar={8} /> // This line should pass typecheck
    }

Consequently, the earlier explanation is incorrect.

Answer №3

The solution to the problem lies in my original code:

import * as React from 'react';

interface FooProps {
  foo: string;
  bar: string;
}

function withProps<P extends FooProps>(
  Comp: React.ComponentType<P>
) {
  return class extends React.Component {
    render() {
      return <Comp foo="foo" bar="bar"/>  // "Comp" typecheck OK
    }
  }
}

The issue was discovered in TypeScript versions 3.1.6 to 3.3-RC: https://github.com/Microsoft/TypeScript/issues/28938

Reverting back to 3.1.4 resolved the problem.

**UPDATE: The code is functional with Typescript version 3.3.0-dev.20190117

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the reason behind the lack of preservation of object state when using Promises?

Here is a code snippet to consider: class ClientWrapper { private client: Client; constructor() { this.client = new Client(); } async connect() : Promise<void> { return this.client.connect(); } async isConne ...

Is it possible for TypeScript to define keys that are permitted to be absent but not able to be assigned the value of `undefined`?

Query Synopsis My aim is to restrict explicit values of undefined while permitting implicit undefined values. Context In the realm of JavaScript, there exists a scenario where attempting to access a nonexistent key results in undefined, similarly, access ...

Tips for minimizing a collection of objects?

Currently, I am working on developing a unique quiz format where there are no correct or incorrect answers; instead, responses are given a score ranging from 1 to 4. Each question falls under one of four distinct categories (such as cat, dog, rabbit, alpa ...

Exploring Ways to Refresh the Appearance of Your Highchart Theme

My code allows me to switch between a light and dark theme for Highcharts, but the changes don't take effect until I refresh the page. Is there a way to update the theme without needing to refresh? ngAfterViewInit(){ Highcharts.setOptions(getOptio ...

Creating a Redis client in Typescript using the `redis.createClient()` function

I'm currently trying to integrate Redis (v4.0.1) into my Express server using TypeScript, but I've encountered a small issue. As I am still in the process of learning TypeScript, I keep getting red underline errors on the host parameter within th ...

Managing Angular subscriptions to prevent memory leaks when making API requests

When I first delved into Angular, a colleague suggested using take(1) for API calls with observables in RxJs, claiming it automatically unsubscribes after one call. Believing this, I continued to use it until noticing significant rendering delays when nav ...

typescript Parameter type dependency based on value is not functioning

interface AddDataRequest{ data:any } interface AddDataResponse{ id:string } interface ITest{ addData(json:AddDataRequest):Promise<AddDataResponse> removeData(json:AddDataResponse):Promise<boolean> } function testInterface<A e ...

Trustpilot Authentication Error: Grant_type Not Recognized

I am attempting to utilize the Trustpilot API for sending email review invitations. Before making the call, I need to obtain an access token as per Trustpilot's documentation. However, when implementing the function below, I am encountering an error i ...

How to efficiently use lunr in typescript?

The Definitely Typed repository demonstrates the importation in this manner: import * as lunr from 'lunr'; However, when attempting to use it in Stackblitz, it results in the following error: lunr is not a function Any ideas on how to resolve ...

A method for modifying the key within a nested array object and then outputting the updated array object

Suppose I have an array called arr1 and an object named arr2 containing a nested array called config. If the key in the object from arr1 matches with an id within the nested config and further within the questions array, then replace that key (in the arr1 ...

Unlocking the potential of NextAuth.js by enhancing the user session with additional database information on authentication

Currently, I am in the process of creating a straightforward credentials sign flow using next-auth ^4.24.5 with a nextjs 14 app. Within my user model, there is a boolean property named 'isAdmin' that I wish to make accessible in my session using ...

What is the method for launching Chrome synchronously in Selenium WebDriver using createSession()?

After executing the code below using Selenium WebDriver to launch a Chrome browser: import { Driver } from 'selenium-webdriver/chrome'; Driver.createSession(); console.log("I've launched!"); I'm encountering an issue where "I've ...

Utilizing Google Sheets as a secure, read-only database for Angular applications without the need to make the sheet accessible to the

Seeking a way to utilize Google Sheets document as a read-only database for my Angular application, I have attempted various methods. However, the challenge with all these approaches is that they necessitate public sharing of the Sheet (accessible to anyon ...

Is there a way for me to define the type of a prop based on the type of another prop?

I'm not entirely certain how to phrase this inquiry, or which terminology to employ, so I'll do my best in presenting it. My intention is to develop a component that functions on an array of elements and triggers a render function for each eleme ...

Utilizing the class decorator pattern in TypeScript with a recursive original class method: A guide

For clarity, I am utilizing the decorator pattern/approach from and not the experimental decorators feature in TypeScript. The scenario involves extending the next method on the main class Foo. Various features are implemented by different classes like B ...

Can TypeScript types be created using multiple comma-separated strings?

Is it feasible to define a custom type in TypeScript like Type LayoutType = "Left" | "Right" | "Top" | "Bottom" | "VCenter", that would combine values such as "Left,VCenter"? Or do I need to create a string literal for every possible combination? ...

Insert a line below the active tab button

I made three buttons for three different tabs. Is there a way to include an underline below the active tab button (under the red "zone")? I'm aiming for something similar to mat-tab. Any assistance is appreciated! DEMO Code viewMode = 'tab ...

Explain a TypeScript function that takes an object as input and returns a new object with only the

Check Playground What's the best way to define a type for this specific function? The inputObject should contain all keys from the enablePropertiesArray and may have additional ones. The function is expected to return a copy of the inputObject, inclu ...

Determine the return type of a related function

My goal is to achieve the following functionality: const columns: ColumnDefinition<Pair>[] = [ { label: 'Pair', value: pair => (all ? pair.code : pair.second.code), format: result => result.toUpperCase() }, { label: 'P ...

Testing in Cypress to determine if one date is half a year before another can be achieved using date manipulation functions and assertions

I'm currently working on testing an SVG graph with dynamic dates on the x-axis. For demonstration purposes, let's assume that the early date is 'Feb 22' and the later date is 'Aug 22'. While I can extract values from each ele ...