Reexport styles from to improve TS performance - #47069
Conversation
Netlify deploy previewBundle size report
|
b3d075e to
e050e50
Compare
|
@siriwatknp Nice approach! The solution looks good from what I’ve seen. Could we optimize the sx prop with theme too ( |
|
Ran the test and can confirm the numbers. If you need some support with some chores around moving from styles to styles optimized and test stuff lmk :) Edit: This fixes a very "weird" Problem with circular Depdencies regarding const theme1 = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
backgroundColor: 'primary.main'
}
}
}
}
});
const theme2 = createTheme(theme1)if you create a theme (theme1) with a const theme1Options: ThemeOptions = {
components: {
MuiButton: {
styleOverrides: {
root: {
backgroundColor: 'primary.main'
}
}
}
}
}
const theme1 = createTheme(theme1Options);
const theme2 = createTheme(theme1Options);
const theme3 = createTheme(theme1 as ThemeOptions)I created an issue for the this behavior, to adress it separatley and would suggest to add a warning to the page. |
I would leave the |
yep that fixes the old Is it ok for the ButtonTheme etc. to be exported from the root i.e. being barrel importable? Or should they only be importable from their subdirectory i.e. |
|
@siriwatknp: any update on this? |
stylesOptimized to improve TS performance|
I would love to see this merged or iterated upon, around 40% of our application's typechecking time is spent on computations related to MUI's |
|
Update after testing this PR more deeply with the built package and the The numbers in the PR are correct. But I found a structural problem with the opt-in model. The optimization turns off when any file imports from The strict types come back through the module augmentation in
The bigger issue: dependencies. MUI X, Toolpad, any theme library on npm imports Other findings from testing:
New direction I want to try: a type-level flag instead of a new entry point. Same pattern as the existing declare module '@mui/material/styles' {
interface TypeFeatures {
optimizedTheme: true;
}
}Inside export interface TypeFeatures {}
// in Theme, ThemeOptions, CssVarsThemeOptions:
components?: TypeFeatures extends { optimizedTheme: true }
? ThemeComponents
: Components<Omit<Theme, 'components'>>;Conditional type branches are lazy, so the strict branch is never instantiated when the flag is on. I prototyped this on the built package of this PR and measured with the same harness:
Flag off is identical to today (144,499 vs 144,498 on this branch). Flag on matches the pristine This removes the need for the Known tradeoffs:
I will open a draft PR with this approach from the current master to compare. |
|
Opened the flag-based alternative as a draft: #49004 Numbers on current master with TypeScript 6.0.3: flag off 145,559 instantiations / 6.24s check, flag on 1,922 / 0.06s. Stray imports and dependencies cannot turn it off. |
closes #42772, closes #47099
For Reviewer
ThemetostylesOptimizedto remove cyclic deps and set theme.components to emptyThemefromstyleswith augmented theme.components to preserve the behaviorstylesOptimizedtostylesso that user can switch the import path without breaking change<component>.d.tsto use from stylesOptimized and export its Theme types for selective augmentationSummary
@mui/material/stylesimport with@mui/material/stylesOptimizedincluding module augmentationTo test the change, checkout this PR:
Then edit the
packages/mui-material/perf-test/test-createTheme.tsxto import createTheme from@mui/material/stylesand run diagnosis again.Compare the result between the two.
Root Cause
The issue stems from circular TypeScript dependency in the type definitions:
Original definition (
packages/mui-material/src/styles/createThemeNoVars.d.ts):The circular path:
ThemeOptions.components→Components<Omit<Theme, 'components'>>ThemeinterfaceThemeextendsBaseThemeandCssVarsProperties, inlining all their type definitionsThemeis referenced back inThemeOptions→ circular dependencyWhy exponential type computation:
The
Components<Theme>interface is massive - for each of 80+ MUI components, it references:When TypeScript resolves
Components<Omit<Theme, 'components'>>:ComponentsOverridesandComponentsVariantsuses theThemegeneric with complexInterpolationtypes@mui/materialMemory spike: From ~460MB to ~2.2GB (4× increase), causing OOM errors in CI/CD
User Journey:
Solution
Created alternative entry point
stylesOptimizedthat breaks circular dependency by moving completeThemedefinition there, makingcreateThemeNoVars.d.tsreference it instead of defining inline.Key Changes
1. New optimized entry point (
packages/mui-material/src/stylesOptimized/createTheme.d.ts):2. Mirror exports (
packages/mui-material/src/stylesOptimized/index.ts):3. Update original definition (
packages/mui-material/src/styles/createThemeNoVars.d.ts):Why This Works
Breaks circular dependency:
stylesOptimized/createTheme.d.tsdefinesThemewith simplecomponents?: ThemeComponents(no generics, no circular references)styles/createThemeNoVars.d.tsextendsThemeOptimizedinstead of defining inlineThemeOptimizedonce (fromstylesOptimized), avoiding repeated instantiationsNon-breaking for v7:
import { ThemeOptions } from '@mui/material/styles'as beforeThemeinterface still usesComponents<T>generic for backward compatibility@mui/material/stylesOptimizedfor better build performancePerformance impact (from analysis):
Usage for Library Authors
To benefit from improved TypeScript performance, replace ALL imports from
@mui/material/styleswith@mui/material/stylesOptimized:Important:
@mui/material/styleswill work but with higher memory usagestylesOptimizedfor CI/CD stabilityResult
Before:
After: