question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

@types/react@17.0.41 breaks forwarding polymorphic component refs

See original GitHub issue

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Current behavior 😯

Given the following code (that wraps a MUI component and uses forwardRef to pass the ref down):

import { forwardRef } from "react";
import Paper, { PaperProps } from "@mui/material/Paper";
import Container from "@mui/material/Container";

export interface HeaderProps
  extends Omit<PaperProps, "elevation" | "square" | "component" | "variant"> {
  elevation?: number;
}

export const Header = forwardRef<HTMLDivElement, HeaderProps>(
  ({ children, elevation = 2, sx, ...props }, ref) => (
    <Paper
      ref={ref}
      component="header"
      square
      elevation={elevation}
      sx={{ padding: 2, ...sx }}
      {...props}
    >
      <Container>{children}</Container>
    </Paper>
  )
);

This code works fine but when you update a project to use @types/react@17.0.41 then the following TS error will display (for the ref={ref} line):

src/Header.tsx:12:5 - error TS2769: No overload matches this call.
  Overload 1 of 2, '(props: { component: "header"; } & { children?: ReactNode; classes?: Partial<PaperClasses> | undefined; elevation?: number | undefined; square?: boolean | undefined; sx?: SxProps<...> | undefined; variant?: "elevation" | ... 1 more ... | undefined; } & CommonProps & Omit<...>): Element', gave the following error.
    Type 'ForwardedRef<HTMLDivElement>' is not assignable to type 'RefCallback<HTMLElement> | RefObject<HTMLElement> | null | undefined'.
      Type '(instance: HTMLDivElement | null) => void' is not assignable to type 'RefCallback<HTMLElement> | RefObject<HTMLElement> | null | undefined'.
        Type '(instance: HTMLDivElement | null) => void' is not assignable to type 'RefCallback<HTMLElement>'.
          Types of parameters 'instance' and 'instance' are incompatible.
            Type 'HTMLElement | null' is not assignable to type 'HTMLDivElement | null'.
              Property 'align' is missing in type 'HTMLElement' but required in type 'HTMLDivElement'.
  Overload 2 of 2, '(props: DefaultComponentProps<PaperTypeMap<{}, "div">>): Element', gave the following error.
    Type '{ children: Element; key?: Key | null | undefined; id?: string | undefined; color?: string | undefined; translate?: "yes" | "no" | undefined; hidden?: boolean | undefined; ... 254 more ...; sx: { ...; } | ... 5 more ... | { ...; }; }' is not assignable to type 'IntrinsicAttributes & { children?: ReactNode; classes?: Partial<PaperClasses> | undefined; elevation?: number | undefined; square?: boolean | undefined; sx?: SxProps<...> | undefined; variant?: "elevation" | ... 1 more ... | undefined; } & CommonProps & Omit<...>'.
      Property 'component' does not exist on type 'IntrinsicAttributes & { children?: ReactNode; classes?: Partial<PaperClasses> | undefined; elevation?: number | undefined; square?: boolean | undefined; sx?: SxProps<...> | undefined; variant?: "elevation" | ... 1 more ... | undefined; } & CommonProps & Omit<...>'.

 12     <Paper
        ~~~~~~
 13       ref={ref}
    ~~~~~~~~~~~~~~~
... 
 18       {...props}
    ~~~~~~~~~~~~~~~~
 19     >
    ~~~~~

  node_modules/typescript/lib/lib.dom.d.ts:6192:5
    6192     align: string;
             ~~~~~
    'align' is declared here.
  node_modules/@types/react/index.d.ts:824:46
    824                     ? PropsWithoutRef<P> & { ref?: Exclude<R, string> | undefined }
                                                     ~~~
    The expected type comes from property 'ref' which is declared here on type 'IntrinsicAttributes & { component: "header"; } & { children?: ReactNode; classes?: Partial<PaperClasses> | undefined; elevation?: number | undefined; square?: boolean | undefined; sx?: SxProps<...> | undefined; variant?: "elevation" | ... 1 more ... | undefined; } & CommonProps & Omit<...>'


Found 1 error in src/Header.tsx:12

Removing component="header" from the props will make the error disappear. So the polymorphic nature of Paper seems to be an issue here.

This works for example:

export const Header = forwardRef<HTMLDivElement, HeaderProps>(
  ({ children, elevation = 2, sx, ...props }, ref) => (
    <Paper
      ref={ref}
      square
      elevation={elevation}
      sx={{ padding: 2, ...sx }}
      {...props}
    >
      <Container>{children}</Container>
    </Paper>
  )
);

But as soon as I add component="header" back it fails. Changing HTMLDivElement to HTMLElement would appear to be the correct way forward but it still errors.

Expected behavior 🤔

No response

Steps to reproduce 🕹

Repo here: https://github.com/robcaldecott/mui-polymorphic-ref-ts-error

Context 🔦

No response

Your environment 🌎

`npx @mui/envinfo`
  System:
    OS: macOS 10.15.7
  Binaries:
    Node: 16.13.2 - ~/.nvm/versions/node/v16.13.2/bin/node
    Yarn: 1.22.17 - ~/.nvm/versions/node/v16.13.2/bin/yarn
    npm: 8.5.2 - ~/.nvm/versions/node/v16.13.2/bin/npm
  Browsers:
    Chrome: 99.0.4844.83
    Edge: Not Found
    Firefox: 96.0.2
    Safari: 14.1.2
  npmPackages:
    @emotion/react: ^11.8.2 => 11.8.2 
    @emotion/styled: ^11.8.1 => 11.8.1 
    @mui/base:  5.0.0-alpha.73 
    @mui/material: ^5.5.2 => 5.5.2 
    @mui/private-theming:  5.4.4 
    @mui/styled-engine:  5.5.2 
    @mui/system:  5.5.2 
    @mui/types:  7.1.3 
    @mui/utils:  5.4.4 
    @types/react: ^17.0.41 => 17.0.41 
    react: ^17.0.2 => 17.0.2 
    react-dom: ^17.0.2 => 17.0.2 
    typescript: ^4.5.4 => 4.6.2 ```
</details>

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
eps1loncommented, Mar 23, 2022

Fixed in @types/react^17.0.42 (might take a bit until released)

1reaction
mnajdovacommented, Mar 22, 2022

Ah, I thought it is head, by bad. This is going to be an interesting issue then. With the latest @types/react changes, as far as I understood you cannot set ref of a subtype to a variable accepting a ref of a supertype (for example HTMLElement as you assumed).

I am adding the use-case as a test in our codebase to ensure it will work with the changes we are preparing.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Forwarding refs for a polymorphic React component in ...
How to properly type polymorphic React components using forwardRef in TypeScript.
Read more >
Build strongly typed polymorphic components with React and ...
Learn how to build strongly typed polymorphic React components with TypeScript, using familiar Chakra UI and MUI component props as guides.
Read more >
Intermediate TypeScript and React Handbook – How to Build ...
Hey everyone! In this detailed guide, I'll show you how to build strongly typed Polymorphic React components with Typescript.
Read more >
Create a Polymorphic Component with Typescript and React
rest}>{children}</Element>; };. Now we wrap the whole thing in forwardRef, add our ref prop, and we're done. const Box: PolymorphicComponent = ...
Read more >
How can I contstrain the props when using Polymorphic ...
Based on this great article, "Forwarding refs for a polymorphic React component in TypeScript" I've been trying to create a Polymorphic ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found