We have extended SnackbarContent component with our custom one (MySnackbarContent):
export interface MySnackbarContentProps extends Omit<SnackbarContentProps, 'variant'> {
variant?: MyCustomVariant;
type?: MyCustomType;
banner?: boolean;
// ...
}
const MySnackbarContent = forwardRef<HTMLElement, MySnackbarContentProps>(props: MySnackbarContentProps, ref) => {
const { variant = 'normal', type = 'default', banner = false, ...other } = props;
const className = ...;
return <SnackbarContent ref={ref} className={className} {...other} />
}
Here is the error that I get when I use the custom SnackbarContent component:
Property 'css' is missing in type '{ type: "error" | "default" | "success" | undefined; message: string; action: Element; }' but required in type 'Pick<MySnackbarContentProps, "hidden" | "style" | "onSelect" | "slot" | "title" | "className" | "classes" | "innerRef" | "defaultChecked" | "defaultValue" | ... 258 more ... | "actionClickHandler">'.
I do not understand why css
property is a problem here because we are not using emotion or styled-component (we are using JSS).
Weirdly, everything types are properly checked when I remove forwardRef
.
A possible way to mitigate this issue is to add css
property in MySnackbarContentProps
and set it to something like: css?: null
. For some reason, css
property is a required property. Not 100% sure why that is the case here. What am I missing here?
EDIT: Explanation for the root cause
There are three parts at play when trying to understand the root cause.
Firstly, Typescript allows augmenting any type and the result of the augmentation will be merging of properties in different definitions. For example, the code below:
interface Person {
name: string;
}
interface Person {
age: number;
}
will be the same as writing the following:
interface Person {
name: string;
age: number
}
Playground
This augmentation does not need to be in the same module/file; so, any module in the project (including node_modules) can augment any type in another module).
Secondly, Material UI allows passing all DOM properties to many of its components. So, it is essentially extending base React.DOMAttributes<T>
(e.g Paper extends React.DOMAttributes<HTMLDivElement>
) in a lot of components.
For me, I was able to find the problem because I knew that css
is a property of emotion
, which is used by Storybook; so, I dug deeper to see what emotion does and found the following: https://github.com/emotion-js/emotion/blob/31e610f2385d5a3dfd532b31f743e5f6b9fee43b/packages/react/types/index.d.ts#L99
So, emotion is augmenting React.DOMAttributes<T>
by adding optional css
property to it. If any type extends from this type, they will have access to css
property. This was enough for me to identify the culprit. The library was exporting a utility that was meant to be used in Storybook (some storybook functions were being imported from those utilities). Removing that component from the output solved the issue. If you are using Storybook, check if the outputted package used any of the Storybook components.
One thing that I did not investigate was why the css
was a mandatory property even though emotion was passing the prop as optional.
Thanks for the follow-up, Gasim !
For me the root issue was also caused by Storybook being bundled with the application, because we're using Storybook files with a *.stories.tsx
extension.
Typescript loads all .tsx
files in our setup, which in turn loads the global Emotion prop augment OP mentioned.
I had to add "exclude": ["**/*.stories.tsx"]
to our tsconfig.json
to fix it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With