material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

[Box] Type error when too many properties exist in JSX.IntrinsicElements

Open Methuselah96 opened this issue 3 years ago • 33 comments

Duplicates

  • [X] I have searched the existing issues

Latest version

  • [X] I have tested the latest version

Current behavior 😯

The Box component produces type errors when too many properties are added to JSX.IntrinsicElements.

Expected behavior 🤔

The Box component should not produce type error when properties are added to JSX.IntrinsicElements.

Steps to reproduce 🕹

When using @mui/material in the same project with @react-three/fiber and @react-three/drei using the Box component produces a type error:

error TS2590: Expression produces a union type that is too complex to represent.
import React from 'react';
import { Box } from '@mui/material';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';

function Root() {
  return (
    <Box sx={{ m: 1 }}>
      <Canvas>
        <OrbitControls />
      </Canvas>
    </Box>
  );
}

Output
import React from 'react';
import { Box } from '@mui/material';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
function Root() {
    return (React.createElement(Box, { sx: { m: 1 } },
        React.createElement(Canvas, null,
            React.createElement(OrbitControls, null))));
}

Compiler Options
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2017",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

Playground Link: Provided

This behavior can also be replicated without any dependence on @react-three/fiber or @react-three/drei by adding properties to JSX.IntrinsicElements:

import React from 'react';
import { Box } from '@mui/material';

declare global {
  namespace JSX {
    interface IntrinsicElements {
      test1: number;
      test2: number;
      test3: number;
      test4: number;
      test5: number;
      test6: number;
      test7: number;
      test8: number;
      test9: number;
      test10: number;
      test11: number;
      test12: number;
      test13: number;
      test14: number;
      test15: number;
      test16: number;
      test17: number;
      test18: number;
      test19: number;
      test20: number;
      test21: number;
      test22: number;
      test23: number;
      test24: number;
      test25: number;
      test26: number;
      test27: number;
      test28: number;
      test29: number;
      test30: number;
      test31: number;
      test32: number;
      test33: number;
      test34: number;
      test35: number;
      test36: number;
      test37: number;
      test38: number;
      test39: number;
      test40: number;
      test41: number;
      test42: number;
      test43: number;
      test44: number;
      test45: number;
      test46: number;
      test47: number;
      test48: number;
      test49: number;
      test50: number;
      test51: number;
      test52: number;
      test53: number;
      test54: number;
      test55: number;
      test56: number;
      test57: number;
      test58: number;
      test59: number;
      test60: number;
      test61: number;
      test62: number;
      test63: number;
      test64: number;
      test65: number;
      test66: number;
      test67: number;
      test68: number;
      test69: number;
      test70: number;
      test71: number;
      test72: number;
      test73: number;
      test74: number;
      test75: number;
      test76: number;
      test77: number;
      test78: number;
      test79: number;
      test80: number;
      test81: number;
      test82: number;
      test83: number;
      test84: number;
      test85: number;
      test86: number;
      test87: number;
      test88: number;
      test89: number;
      test90: number;
      test91: number;
      test92: number;
      test93: number;
      test94: number;
      test95: number;
      test96: number;
      test97: number;
      test98: number;
      test99: number;
      test100: number;
      test101: number;
      test102: number;
      test103: number;
      test104: number;
      test105: number;
      test106: number;
      test107: number;
      test108: number;
      test109: number;
      test110: number;
      test111: number;
      test112: number;
      test113: number;
      test114: number;
      test115: number;
      test116: number;
      test117: number;
      test118: number;
      test119: number;
      test120: number;
      test121: number;
      test122: number;
      test123: number;
      test124: number;
      test125: number;
      test126: number;
      test127: number;
      test128: number;
      test129: number;
      test130: number;
      test131: number;
      test132: number;
      test133: number;
      test134: number;
      test135: number;
      test136: number;
      test137: number;
      test138: number;
      test139: number;
    }
  }
}

function Root() {
  return <Box sx={{ m: 1 }} />;
}

Output
import React from 'react';
import { Box } from '@mui/material';
function Root() {
    return React.createElement(Box, { sx: { m: 1 } });
}

Compiler Options
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "strictBindCallApply": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "declaration": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "ES2017",
    "jsx": "react",
    "module": "ESNext",
    "moduleResolution": "node"
  }
}

Playground Link: Provided

Context 🔦

This is primarily in the context of trying to use @react-three/fiber and @react-three/drei in the same project as @mui/material. Related issues include https://github.com/mui/material-ui/issues/29176 and https://stackoverflow.com/questions/68692230/ts-expression-produces-a-union-type-that-is-too-complex-to-represent-with-materi. The latter issue seems to suggest this has been resolved, but I do not believe it has been.

Your environment 🌎

tsconfig.json included above.

Methuselah96 avatar Aug 25 '22 12:08 Methuselah96

For those facing this issue, I was able to workaround it by patching Box.d.ts like so:

  *
  * - [Box API](https://mui.com/material-ui/api/box/)
  */
-declare const Box: OverridableComponent<BoxTypeMap>;
+declare const Box: <D extends React.ElementType = 'div'>(props: BoxProps<D> & { component?: D }) => JSX.Element;
 
 export type BoxProps<
   D extends React.ElementType = BoxTypeMap['defaultComponent'],

I'm not sure if this is a good long-term fix, but it seems to work from what I can tell. I suppose the way OverridableComponent is defined produces too complex of a union type.

Methuselah96 avatar Aug 26 '22 01:08 Methuselah96

The above solution does seem to lose automatic typing for event handlers (i.e., the type of the event argument for onContextMenu for a normal div Box is not automatically inferred), but it is much better then it not being able to type-check at all.

Methuselah96 avatar Aug 26 '22 02:08 Methuselah96

I have the same problem with the latest version (npm).

I created a component for a workaround if anyone wants to use it:

// ** MUI Imports
import MuiBox from '@mui/material/Box'
import {BoxProps} from '@mui/material/Box'

const Box = (props: BoxProps) => {
  return (
    <MuiBox
      {...props}
      component="div"
    />
  )
}

export type {BoxProps}

export default Box

fishuke avatar Oct 24 '22 11:10 fishuke

Does it happen with only Box component?

siriwatknp avatar Dec 19 '22 11:12 siriwatknp

Yes.

Methuselah96 avatar Dec 19 '22 12:12 Methuselah96

Any news on this? I haven't found any solution to this, only workarounds.

mpn7 avatar Jan 03 '23 10:01 mpn7

Any news on this? I haven't found any solution to this, only workarounds.

I'm working on a large code base and changing the imports or updating the component type everywhere is not deemed acceptable, so I am also still looking for a real solution.

zaesur avatar Jan 25 '23 12:01 zaesur

I have a workaround by patching the Box.d.ts type in @mui/material, @mui/system, and @material-core with patch-package and replacing the types with

-declare const Box: OverridableComponent<BoxTypeMap>;
+// declare const Box: OverridableComponent<BoxTypeMap>;
 
 export type BoxProps<
   D extends React.ElementType = BoxTypeMap['defaultComponent'],
   P = {},
 > = OverrideProps<BoxTypeMap<P, D>, D>;
 
-export default Box;
+export default function Box(props: BoxProps): JSX.Element;

shinitakunai avatar Feb 18 '23 10:02 shinitakunai

I have a workaround by patching the Box.d.ts type in @mui/material, @mui/system, and @material-core with patch-package and replacing the types with

-declare const Box: OverridableComponent<BoxTypeMap>;
+// declare const Box: OverridableComponent<BoxTypeMap>;
 
 export type BoxProps<
   D extends React.ElementType = BoxTypeMap['defaultComponent'],
   P = {},
 > = OverrideProps<BoxTypeMap<P, D>, D>;
 
-export default Box;
+export default function Box(props: BoxProps): JSX.Element;

Worked fine for me and I think it's the cleaner workaround for now

fgarrec0397 avatar Feb 19 '23 18:02 fgarrec0397

I have a workaround by patching the Box.d.ts type in @mui/material, @mui/system, and @material-core with patch-package and replacing the types with

-declare const Box: OverridableComponent<BoxTypeMap>;
+// declare const Box: OverridableComponent<BoxTypeMap>;
 
 export type BoxProps<
   D extends React.ElementType = BoxTypeMap['defaultComponent'],
   P = {},
 > = OverrideProps<BoxTypeMap<P, D>, D>;
 
-export default Box;
+export default function Box(props: BoxProps): JSX.Element;

This update was involved on @mui/material^5.11.10?

luckysw0rd avatar Feb 27 '23 13:02 luckysw0rd

With the mui 5.14.1 update this error occurs on even more components, e.g. ToggleButton, Typography, Card, Divider, etc. Version 5.14.0 was fine.

MDransfeld avatar Jul 20 '23 06:07 MDransfeld

Yeah, while with 5.14.0 it only concerned Box and it was quite ok to work around this, with 5.14.1 it seems like a real issue/problem concerning many Mui components.

schimi-dev avatar Jul 20 '23 10:07 schimi-dev

It looks like this was caused by these added lines in https://github.com/mui/material-ui/pull/35924. The patch to fix it would be to remove the added lines in OverrideComponent.d.ts, but that obviously runs counter to the point of the PR.

Methuselah96 avatar Jul 20 '23 12:07 Methuselah96

@michaldudak Can you reopen this issue, since the fix was reverted in https://github.com/mui/material-ui/pull/38356?

Methuselah96 avatar Aug 12 '23 21:08 Methuselah96

Yes, of course.

michaldudak avatar Aug 14 '23 09:08 michaldudak

Hello, we've got similar issue at Textfield component: image

michalmuchakr avatar Aug 22 '23 13:08 michalmuchakr

@michalmuchakr What version are you on? This error is known to occur for other components in 5.14.1, but has since been fixed.

Methuselah96 avatar Aug 22 '23 17:08 Methuselah96

I'm using "@mui/material": "5.14.5".

michalmuchakr avatar Aug 23 '23 07:08 michalmuchakr

@michalmuchakr, could you create a codesandbox showing the issue? I'm surprised the problem exists in TextField.

michaldudak avatar Aug 25 '23 20:08 michaldudak

We experienced this issue in joy-ui but somehow it just happened yesterday and we've been updating the betas on each new release 🤷 . Now when I roll back I can still see the issue. After a lot of refactoring we had to remove ALL usages of Box and mostly use this now:

const Box = styled("div")({}) // Some style

So generally Box is completely broken for us.

This cost us a lot of nerv and unplanned refactoring.

Please prioritize this issue as it can/will break a lot of big projects.

I'm a bit scared to check which new bugs were introduced by the refactoring but hopefully our tests will catch them.

❤️


EDIT: this is probably helpful ;)

    "@mui/base": "5.0.0-beta.20",
    "@mui/icons-material": "5.14.14",
    "@mui/joy": "5.0.0-beta.11",
    "@mui/material": "5.14.14",

pixelass avatar Oct 19 '23 11:10 pixelass

@pixelass Does the issue exist when you explicitly provide a component prop to the Box?

michaldudak avatar Oct 27 '23 08:10 michaldudak

@pixelass Does the issue exist when you explicitly provide a component prop to the Box?

It seems to have happened after the project reached a certain size. Not sure if that makes sense.

Unrelated to whether props were added or not. Suddenly ALL instances were affected.

pixelass avatar Oct 27 '23 10:10 pixelass

To be clear - the instances of the Box where you have both the sx and component prop (such as <Box sx={{ m: 1 }} component='div' />) trigger a TypeScript error, yes?

michaldudak avatar Oct 27 '23 12:10 michaldudak

@michaldudak all instances of Box suddenly triggered that issue.

<Box>Foo</Box>
<Box sx={{m: 2}}>Foo</Box>
<Box component="footer">Foo</Box>
<Box data-testid="foo">Foo</Box>

I would have to checkout an older version of a closed source project to be sure though. I just did a find & replace at some point, since they started popping up one after another.

I would love to create a repro, but I will have to see when I find time, since this is a rather strange and somewhat hard to reproduce.

pixelass avatar Oct 27 '23 13:10 pixelass

@michaldudak Out of nothing this just happened: image

Even a simple self closing Box causes the same issue.

image

No props on the box. I just edited a totally unrelated file (which is not even imported in any way) Very spooky

pixelass avatar Nov 02 '23 14:11 pixelass

ANd here we go again image image image

pixelass avatar Nov 14 '23 17:11 pixelass

I haven't noticed it on my own. A repro would help us investigate this issue a lot.

michaldudak avatar Nov 23 '23 17:11 michaldudak

After checking the OP of this thread I added fiber and drei from react-three

Check the pages/index.tsx of this repo:

https://github.com/pixelass/joy-box-repro

image

pixelass avatar Nov 26 '23 09:11 pixelass

This does make sense since it is highly likely that I added react-three/fiber in the affected repositories

pixelass avatar Nov 26 '23 09:11 pixelass

Seems like a simple import of react-three causes this issue (as suggested in OP extending the IntrinsicElements namespace):

image image image image

pixelass avatar Nov 26 '23 09:11 pixelass