Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[styled-engine] Fix style overrides variants type #45478

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

siriwatknp
Copy link
Member

@siriwatknp siriwatknp commented Mar 4, 2025

Closes #45275

The issue is that Omit<CSSOthersObject, 'variants'> doesn't work as expected as CSSOthersObject is a mapped type, which Omit has known issues with:

In a nutshell: By design, Omit won't remove variants from CSSOthersObject, so the type defined in CSSObjectWithVariants is not used.

Solution and discussion below

@mui-bot
Copy link

mui-bot commented Mar 4, 2025

Netlify deploy preview

https://deploy-preview-45478--material-ui.netlify.app/

Bundle size report

No bundle size changes (Toolpad)
No bundle size changes

Generated by 🚫 dangerJS against e91681e

@@ -42,7 +42,7 @@ const items = [
const Chip = styled(MuiChip)(({ theme }) => ({
variants: [
{
props: ({ selected }) => selected,
props: ({ selected }) => !!selected,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have to fix this because the type of selected is boolean | undefined.

Should we loosen the type like of props? to be () => boolean | null | undefined?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't mind the !! to cast into boolean

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean this is a breaking change?

| ComponentSelector
| Keyframes
| SerializedStyles
| CSSPropertiesWithMultiValues
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a myth to me. If not adding CSSPropertiesWithMultiValues explicitly as a union, the spec fail for recreating nested theme.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean with "myth"?

Copy link
Member Author

@siriwatknp siriwatknp Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first, I thought that CSSObject is enough because it extends CSSPropertiesWithMultiValues. However, if I excluded CSSPropertiesWithMultiValues from the union type, I got this error from createTheme.spec:

image

Here is the full error:

Argument of type 'Theme' is not assignable to parameter of type 'Omit<ThemeOptions, "components"> & Pick<CssVarsThemeOptions, "colorSchemes" | "defaultColorScheme" | "components"> & { ...; }'.
  Type 'Theme' is not assignable to type 'Pick<CssVarsThemeOptions, "colorSchemes" | "defaultColorScheme" | "components">'.
    Types of property 'components' are incompatible.
      Type 'Components<BaseTheme> | undefined' is not assignable to type 'Components<Omit<Theme, "palette" | "components"> & CssVarsTheme> | undefined'.
        Type 'Components<BaseTheme>' is not assignable to type 'Components<Omit<Theme, "palette" | "components"> & CssVarsTheme>'.
          Types of property 'MuiAlert' are incompatible.
            Type '{ defaultProps?: Partial<AlertProps> | undefined; styleOverrides?: Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", BaseTheme>> | undefined; variants?: { ...; }[] | undefined; } | undefined' is not assignable to type '{ defaultProps?: Partial<AlertProps> | undefined; styleOverrides?: Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", Omit<Theme, "palette" | "components"> & CssVarsTheme>> | undefined; variants?: { ...; }[] | undefined; } | undefined'.
              Type '{ defaultProps?: Partial<AlertProps> | undefined; styleOverrides?: Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", BaseTheme>> | undefined; variants?: { ...; }[] | undefined; }' is not assignable to type '{ defaultProps?: Partial<AlertProps> | undefined; styleOverrides?: Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", Omit<Theme, "palette" | "components"> & CssVarsTheme>> | undefined; variants?: { ...; }[] | undefined; }'.
                Types of property 'styleOverrides' are incompatible.
                  Type 'Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", BaseTheme>> | undefined' is not assignable to type 'Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", Omit<Theme, "palette" | "components"> & CssVarsTheme>> | undefined'.
                    Type 'Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", BaseTheme>>' is not assignable to type 'Partial<OverridesStyleRules<keyof AlertClasses, "MuiAlert", Omit<Theme, "palette" | "components"> & CssVarsTheme>>'.
                      Types of property 'root' are incompatible.
                        Type 'Interpolation<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { theme: BaseTheme; }>' is not assignable to type 'Interpolation<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { theme: Omit<Theme, "palette" | "components"> & CssVarsTheme; }>'.
                          Type 'CSSObject & { variants?: { props: Partial<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { ...; }> | ((props: Partial<...> & { ...; }) => boolean); style: CSSObject | ((args: { ...; }) => CSSObject); }[] | undefined; }' is not assignable to type 'Interpolation<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { theme: Omit<Theme, "palette" | "components"> & CssVarsTheme; }>'.
                            Type 'CSSObject & { variants?: { props: Partial<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { ...; }> | ((props: Partial<...> & { ...; }) => boolean); style: CSSObject | ((args: { ...; }) => CSSObject); }[] | undefined; }' is not assignable to type 'Keyframes'.
                              Type 'CSSObject & { variants?: { props: Partial<AlertProps & Record<string, unknown> & { ownerState: AlertProps & Record<string, unknown>; } & { ...; }> | ((props: Partial<...> & { ...; }) => boolean); style: CSSObject | ((args: { ...; }) => CSSObject); }[] | undefined; }' is missing the following properties from type '{ name: string; styles: string; anim: number; toString: () => string; }': name, styles, animts(2345)

I don't fully understand why.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is not related too much to the type that's being added, this is suspicious, let me check it again locally and I will report back.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on this type: https://github.com/mui/material-ui/blob/master/packages/mui-material/src/styles/createTheme.ts#L38 the error is expected in my opinion. The issue is that in createTheme, we return this type: https://github.com/mui/material-ui/blob/master/packages/mui-material/src/styles/createThemeNoVars.d.ts#L82 which has different definition for components than the input of createTheme. We should probably loosen the input type for createTheme, to accept a Theme, if we are allowing this use-case. We should also make sure that all proposed options from https://mui.com/material-ui/customization/theming/#nesting-the-theme are type safe.

Copy link
Member

@DiegoAndai DiegoAndai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a look at this @siriwatknp! 😊

I have a couple of questions

| Keyframes
| SerializedStyles
| CSSPropertiesWithMultiValues
| (CSSObject & {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a benefit in inlining the variants here instead of defining it separately?

Copy link
Member Author

@siriwatknp siriwatknp Mar 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is required, otherwise variants is not type-safety. You can try in this Playground

@DiegoAndai
Copy link
Member

Hey @siriwatknp! I took a look at this, and I managed to remove CSSPropertiesWithMultiValues from Interpolation by removing components from the Theme interface, see daa9579.

The CssVarsTheme and Joy's Theme types do not have the components property, so I wonder if it being included in Material's Theme was a mistake.

  1. Do you think this is the correct approach?
  2. This fixed the createTheme(theme issue, but it brought up a bunch of other ones 😅 I will continue taking a look on Monday

@siriwatknp
Copy link
Member Author

siriwatknp commented Mar 10, 2025

Hey @siriwatknp! I took a look at this, and I managed to remove CSSPropertiesWithMultiValues from Interpolation by removing components from the Theme interface, see daa9579.

I suggest not removing it as it will introduce another issue for people who read theme.components.* like this:

const theme = createTheme({
  component: {}
})

console.log(theme.components); // << TS error.

Also, removing the components is technically incorrect because the theme object contains components node (in JS).

@DiegoAndai DiegoAndai changed the title [POC] Fix Interpolation types styled-engine] Fix style overrides variants type Mar 14, 2025
@DiegoAndai DiegoAndai changed the title styled-engine] Fix style overrides variants type [styled-engine] Fix style overrides variants type Mar 14, 2025
@DiegoAndai DiegoAndai requested a review from mnajdova March 14, 2025 19:09
@DiegoAndai DiegoAndai self-assigned this Mar 14, 2025
@DiegoAndai DiegoAndai marked this pull request as ready for review March 14, 2025 19:09
@DiegoAndai
Copy link
Member

@siriwatknp I closed #45346 in favor of this PR, lets move forward with this solution

@mnajdova may I ask you to review this?

Copy link
Member

@mnajdova mnajdova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will report soon with my findings.

@@ -42,7 +42,7 @@ const items = [
const Chip = styled(MuiChip)(({ theme }) => ({
variants: [
{
props: ({ selected }) => selected,
props: ({ selected }) => !!selected,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean this is a breaking change?

@@ -218,6 +218,7 @@ const theme = createTheme();
variants: [
{
props: ({ ownerState }) => ownerState.color === 'primary',
style: {},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't style required before?

| ComponentSelector
| Keyframes
| SerializedStyles
| CSSPropertiesWithMultiValues
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error is not related too much to the type that's being added, this is suspicious, let me check it again locally and I will report back.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Variants not type safe anymore in createTheme
4 participants