Skip to content

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 48dda59

@@ -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?

Copy link
Member

Choose a reason for hiding this comment

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

I would argue it's a fix as it was always supposed to be () => boolean (it's only boolean in the CSSObjectWithVariants type), but it wasn't actually because the type wasn't being correctly picked up.

It's one of those fixes that could be perceived as a breaking change.

Maybe we could compromise with a minor release and an explanation on the changelog?

Copy link
Member

Choose a reason for hiding this comment

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

This would be the last thing that we need to decide on. Similar to the styles now being required, this is a fix that might be perceived as breaking.

My proposal is that we release it with in a minor version and a note.

Copy link
Member Author

Choose a reason for hiding this comment

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

My proposal is that we release it with in a minor version and a note.

Let's do that.

| 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 Apr 2, 2025

Choose a reason for hiding this comment

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

We discussed loosening createTheme's input type in the past, let's try it out @siriwatknp.

Copy link
Member Author

Choose a reason for hiding this comment

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

@DiegoAndai I tried loosen the type and include the Theme but it produces bigger errors in the existing spec files.
At this point, I think having the CSSPropertiesWithMultiValues without loosen the type might be the safest in my opinion.

Copy link
Member

Choose a reason for hiding this comment

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

Looks like this change is causing major issues on mui-x styled usages with callback props:
mui/mui-x#17802
@siriwatknp, could you check if there is some oversight in the type? 🙏

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, I am taking a look.

Copy link
Member Author

Choose a reason for hiding this comment

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

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

@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?

Copy link
Member

Choose a reason for hiding this comment

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

It was, but the CSSObjectWithVariants wasn't being picked correctly, so it wasn't enforced. Is that correct @siriwatknp?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. that's correct.

| 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.

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.

LGTM, let's release the fix.

@mnajdova FYI:

  • There are some type changes that might be perceived as "breaking":
    • Variants' props is now correctly typed as () => boolean
    • Variants' styles is now correctly required

These were always the intended types, but they weren't being used, which is the bug we're fixing now. Because they're now correctly used, this effectively changed the resolved types.

Because these are the correct types, Jun and I think this is a fix worth releasing in v7. But because it can be perceived as breaking, the compromise is to release it in a minor bump and explain it in the CHANGELOG. Adapting to these changes is straightforward.

In the worst case scenario, if too many users complain, plan B is to loosen these types in a patch version after the minor one.

@siriwatknp siriwatknp merged commit ef07974 into mui:master Apr 11, 2025
22 checks passed
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
5 participants