From f305df7a3ca9eb10f94bd3a54880a59be2a54648 Mon Sep 17 00:00:00 2001 From: Janpot <2109932+Janpot@users.noreply.github.com> Date: Fri, 22 May 2026 23:01:53 +0200 Subject: [PATCH 1/2] [code-infra] Convert @mui/private-theming to TypeScript Convert the package from hand-written `.js` + `.d.ts` to true TypeScript (single `.ts`/`.tsx` source, declarations emitted by `tsc`), removing the `--skipTsc` build. The exported type surface is unchanged, verified with a bidirectional type-equivalence probe; `unstable_nested` stays a runtime-only export via `@internal` + `stripInternal`. --- packages/mui-private-theming/package.json | 7 ++--- .../src/ThemeProvider/ThemeProvider.d.ts | 9 ------- ...rovider.test.js => ThemeProvider.test.tsx} | 26 +++++++++---------- .../{ThemeProvider.js => ThemeProvider.tsx} | 20 ++++++++++---- .../src/ThemeProvider/index.d.ts | 2 -- .../src/ThemeProvider/index.js | 2 -- .../src/ThemeProvider/index.ts | 4 +++ .../ThemeProvider/{nested.js => nested.ts} | 0 .../src/defaultTheme/index.js | 1 - .../src/defaultTheme/{index.d.ts => index.ts} | 0 packages/mui-private-theming/src/index.d.ts | 7 ----- .../src/{index.js => index.ts} | 2 ++ .../{ThemeContext.js => ThemeContext.ts} | 2 +- .../src/useTheme/index.d.ts | 2 -- .../src/useTheme/{index.js => index.ts} | 0 .../src/useTheme/useTheme.d.ts | 3 --- .../{useTheme.test.js => useTheme.test.tsx} | 6 ++--- .../src/useTheme/{useTheme.js => useTheme.ts} | 3 ++- .../mui-private-theming/tsconfig.build.json | 18 +++++++++++++ packages/mui-private-theming/tsconfig.json | 5 ++++ packages/mui-system/tsconfig.build.json | 1 + pnpm-lock.yaml | 3 +++ 22 files changed, 71 insertions(+), 52 deletions(-) delete mode 100644 packages/mui-private-theming/src/ThemeProvider/ThemeProvider.d.ts rename packages/mui-private-theming/src/ThemeProvider/{ThemeProvider.test.js => ThemeProvider.test.tsx} (78%) rename packages/mui-private-theming/src/ThemeProvider/{ThemeProvider.js => ThemeProvider.tsx} (78%) delete mode 100644 packages/mui-private-theming/src/ThemeProvider/index.d.ts delete mode 100644 packages/mui-private-theming/src/ThemeProvider/index.js create mode 100644 packages/mui-private-theming/src/ThemeProvider/index.ts rename packages/mui-private-theming/src/ThemeProvider/{nested.js => nested.ts} (100%) delete mode 100644 packages/mui-private-theming/src/defaultTheme/index.js rename packages/mui-private-theming/src/defaultTheme/{index.d.ts => index.ts} (100%) delete mode 100644 packages/mui-private-theming/src/index.d.ts rename packages/mui-private-theming/src/{index.js => index.ts} (79%) rename packages/mui-private-theming/src/useTheme/{ThemeContext.js => ThemeContext.ts} (76%) delete mode 100644 packages/mui-private-theming/src/useTheme/index.d.ts rename packages/mui-private-theming/src/useTheme/{index.js => index.ts} (100%) delete mode 100644 packages/mui-private-theming/src/useTheme/useTheme.d.ts rename packages/mui-private-theming/src/useTheme/{useTheme.test.js => useTheme.test.tsx} (77%) rename packages/mui-private-theming/src/useTheme/{useTheme.js => useTheme.ts} (81%) create mode 100644 packages/mui-private-theming/tsconfig.build.json diff --git a/packages/mui-private-theming/package.json b/packages/mui-private-theming/package.json index 16ba33c4ec6cfa..a6f37d48d35393 100644 --- a/packages/mui-private-theming/package.json +++ b/packages/mui-private-theming/package.json @@ -24,7 +24,7 @@ "url": "https://opencollective.com/mui-org" }, "scripts": { - "build": "code-infra build --flat --skipTsc", + "build": "code-infra build --flat", "release": "pnpm build && pnpm publish", "test": "pnpm --workspace-root test:unit --project \"*:@mui/private-theming\"", "typescript": "tsc -p tsconfig.json" @@ -37,6 +37,7 @@ "devDependencies": { "@mui/types": "workspace:^", "@types/chai": "5.2.3", + "@types/prop-types": "15.7.15", "@types/react": "19.2.14", "chai": "6.2.2", "react": "19.2.6" @@ -59,7 +60,7 @@ "node": ">=14.0.0" }, "exports": { - ".": "./src/index.js", - "./*": "./src/*/index.js" + ".": "./src/index.ts", + "./*": "./src/*/index.ts" } } diff --git a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.d.ts b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.d.ts deleted file mode 100644 index 3c2bfd3bbeeec4..00000000000000 --- a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { DefaultTheme } from '../defaultTheme'; - -export interface ThemeProviderProps { - children?: React.ReactNode; - theme: Partial | ((outerTheme: Theme) => Theme); -} -export default function ThemeProvider( - props: ThemeProviderProps, -): React.ReactElement>; diff --git a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.js b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.tsx similarity index 78% rename from packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.js rename to packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.tsx index 82ea191c13e33b..e5af09336d5460 100644 --- a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.js +++ b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.test.tsx @@ -12,10 +12,10 @@ describe('ThemeProvider', () => { const { render } = createRenderer(); it('should provide the theme', () => { - const ref = React.createRef(); - const text = () => ref.current.textContent; + const ref = React.createRef(); + const text = () => ref.current!.textContent; function Test() { - const theme = useTheme(); + const theme = useTheme>(); return {theme.foo}; } @@ -29,10 +29,10 @@ describe('ThemeProvider', () => { }); it('should merge the themes', () => { - const ref = React.createRef(); - const text = () => ref.current.textContent; + const ref = React.createRef(); + const text = () => ref.current!.textContent; function Test() { - const theme = useTheme(); + const theme = useTheme>(); return ( @@ -53,11 +53,11 @@ describe('ThemeProvider', () => { }); it('should memoize the merged output', () => { - const ref = React.createRef(); - const getRenderCountRef = React.createRef(); - const text = () => ref.current.textContent; + const ref = React.createRef(); + const getRenderCountRef = React.createRef<() => number>(); + const text = () => ref.current!.textContent; function Test() { - const theme = useTheme(); + const theme = useTheme>(); return ( @@ -85,14 +85,14 @@ describe('ThemeProvider', () => { expect(text()).to.equal('foobar'); setProps({}); expect(text()).to.equal('foobar'); - expect(getRenderCountRef.current()).to.equal(2); + expect(getRenderCountRef.current!()).to.equal(2); }); describe('warnings', () => { it('should warn about missing provider', () => { expect(() => { render( - theme}> + theme}>
, ); @@ -106,7 +106,7 @@ describe('ThemeProvider', () => { expect(() => { render( - {}}> + {}) as any}>
, diff --git a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.js b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx similarity index 78% rename from packages/mui-private-theming/src/ThemeProvider/ThemeProvider.js rename to packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx index 1abe1b224e32fb..8bccbe66f045e5 100644 --- a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.js +++ b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx @@ -1,12 +1,18 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import exactProp from '@mui/utils/exactProp'; +import type { DefaultTheme } from '../defaultTheme'; import ThemeContext from '../useTheme/ThemeContext'; import useTheme from '../useTheme'; import nested from './nested'; +export interface ThemeProviderProps { + children?: React.ReactNode; + theme: Partial | ((outerTheme: Theme) => Theme); +} + // To support composition of theme. -function mergeOuterLocalTheme(outerTheme, localTheme) { +function mergeOuterLocalTheme(outerTheme: any, localTheme: any) { if (typeof localTheme === 'function') { const mergedTheme = localTheme(outerTheme); @@ -32,9 +38,11 @@ function mergeOuterLocalTheme(outerTheme, localTheme) { * It makes the `theme` available down the React tree thanks to React context. * This component should preferably be used at **the root of your component tree**. */ -function ThemeProvider(props) { +function ThemeProvider( + props: ThemeProviderProps, +): React.ReactElement> { const { children, theme: localTheme } = props; - const outerTheme = useTheme(); + const outerTheme: T | null = useTheme(); if (process.env.NODE_ENV !== 'production') { if (outerTheme === null && typeof localTheme === 'function') { @@ -65,7 +73,7 @@ function ThemeProvider(props) { return {children}; } -ThemeProvider.propTypes = { +(ThemeProvider as any).propTypes /* remove-proptypes */ = { /** * Your component tree. */ @@ -77,7 +85,9 @@ ThemeProvider.propTypes = { }; if (process.env.NODE_ENV !== 'production') { - ThemeProvider.propTypes = exactProp(ThemeProvider.propTypes); + (ThemeProvider as any).propTypes /* remove-proptypes */ = exactProp( + (ThemeProvider as any).propTypes, + ); } export default ThemeProvider; diff --git a/packages/mui-private-theming/src/ThemeProvider/index.d.ts b/packages/mui-private-theming/src/ThemeProvider/index.d.ts deleted file mode 100644 index 2c1d691844b267..00000000000000 --- a/packages/mui-private-theming/src/ThemeProvider/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './ThemeProvider'; -export * from './ThemeProvider'; diff --git a/packages/mui-private-theming/src/ThemeProvider/index.js b/packages/mui-private-theming/src/ThemeProvider/index.js deleted file mode 100644 index 0d1fc6c15c46ab..00000000000000 --- a/packages/mui-private-theming/src/ThemeProvider/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './ThemeProvider'; -export { default as unstable_nested } from './nested'; diff --git a/packages/mui-private-theming/src/ThemeProvider/index.ts b/packages/mui-private-theming/src/ThemeProvider/index.ts new file mode 100644 index 00000000000000..1d096fc5bcd6aa --- /dev/null +++ b/packages/mui-private-theming/src/ThemeProvider/index.ts @@ -0,0 +1,4 @@ +export { default } from './ThemeProvider'; +export type * from './ThemeProvider'; +/** @internal Runtime-only re-export, intentionally absent from the public types. */ +export { default as unstable_nested } from './nested'; diff --git a/packages/mui-private-theming/src/ThemeProvider/nested.js b/packages/mui-private-theming/src/ThemeProvider/nested.ts similarity index 100% rename from packages/mui-private-theming/src/ThemeProvider/nested.js rename to packages/mui-private-theming/src/ThemeProvider/nested.ts diff --git a/packages/mui-private-theming/src/defaultTheme/index.js b/packages/mui-private-theming/src/defaultTheme/index.js deleted file mode 100644 index cb0ff5c3b541f6..00000000000000 --- a/packages/mui-private-theming/src/defaultTheme/index.js +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/packages/mui-private-theming/src/defaultTheme/index.d.ts b/packages/mui-private-theming/src/defaultTheme/index.ts similarity index 100% rename from packages/mui-private-theming/src/defaultTheme/index.d.ts rename to packages/mui-private-theming/src/defaultTheme/index.ts diff --git a/packages/mui-private-theming/src/index.d.ts b/packages/mui-private-theming/src/index.d.ts deleted file mode 100644 index 901fd02a02f9db..00000000000000 --- a/packages/mui-private-theming/src/index.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { default as ThemeProvider } from './ThemeProvider'; -export * from './ThemeProvider'; - -export { default as useTheme } from './useTheme'; -export * from './useTheme'; - -export * from './defaultTheme'; diff --git a/packages/mui-private-theming/src/index.js b/packages/mui-private-theming/src/index.ts similarity index 79% rename from packages/mui-private-theming/src/index.js rename to packages/mui-private-theming/src/index.ts index 051a3a055682e9..62630abb0dd687 100644 --- a/packages/mui-private-theming/src/index.js +++ b/packages/mui-private-theming/src/index.ts @@ -2,3 +2,5 @@ export { default as ThemeProvider } from './ThemeProvider'; export * from './ThemeProvider'; export { default as useTheme } from './useTheme'; + +export type * from './defaultTheme'; diff --git a/packages/mui-private-theming/src/useTheme/ThemeContext.js b/packages/mui-private-theming/src/useTheme/ThemeContext.ts similarity index 76% rename from packages/mui-private-theming/src/useTheme/ThemeContext.js rename to packages/mui-private-theming/src/useTheme/ThemeContext.ts index 59f9db29f9ea6f..dbcefe86f5e76c 100644 --- a/packages/mui-private-theming/src/useTheme/ThemeContext.js +++ b/packages/mui-private-theming/src/useTheme/ThemeContext.ts @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -const ThemeContext = React.createContext(null); +const ThemeContext = React.createContext(null); if (process.env.NODE_ENV !== 'production') { ThemeContext.displayName = 'ThemeContext'; diff --git a/packages/mui-private-theming/src/useTheme/index.d.ts b/packages/mui-private-theming/src/useTheme/index.d.ts deleted file mode 100644 index bbe4d936c03acd..00000000000000 --- a/packages/mui-private-theming/src/useTheme/index.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from './useTheme'; -export * from './useTheme'; diff --git a/packages/mui-private-theming/src/useTheme/index.js b/packages/mui-private-theming/src/useTheme/index.ts similarity index 100% rename from packages/mui-private-theming/src/useTheme/index.js rename to packages/mui-private-theming/src/useTheme/index.ts diff --git a/packages/mui-private-theming/src/useTheme/useTheme.d.ts b/packages/mui-private-theming/src/useTheme/useTheme.d.ts deleted file mode 100644 index a3aa176fa57a85..00000000000000 --- a/packages/mui-private-theming/src/useTheme/useTheme.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DefaultTheme } from '../defaultTheme'; - -export default function useTheme(): T; diff --git a/packages/mui-private-theming/src/useTheme/useTheme.test.js b/packages/mui-private-theming/src/useTheme/useTheme.test.tsx similarity index 77% rename from packages/mui-private-theming/src/useTheme/useTheme.test.js rename to packages/mui-private-theming/src/useTheme/useTheme.test.tsx index 14cfdcb748d950..c94e86d80664ba 100644 --- a/packages/mui-private-theming/src/useTheme/useTheme.test.js +++ b/packages/mui-private-theming/src/useTheme/useTheme.test.tsx @@ -8,10 +8,10 @@ describe('useTheme', () => { const { render } = createRenderer(); it('should use the theme', () => { - const ref = React.createRef(); - const text = () => ref.current.textContent; + const ref = React.createRef(); + const text = () => ref.current!.textContent; function Test() { - const theme = useTheme(); + const theme = useTheme>(); return {theme.foo}; } diff --git a/packages/mui-private-theming/src/useTheme/useTheme.js b/packages/mui-private-theming/src/useTheme/useTheme.ts similarity index 81% rename from packages/mui-private-theming/src/useTheme/useTheme.js rename to packages/mui-private-theming/src/useTheme/useTheme.ts index 74e7bc0bef671a..0184306294bcf4 100644 --- a/packages/mui-private-theming/src/useTheme/useTheme.js +++ b/packages/mui-private-theming/src/useTheme/useTheme.ts @@ -1,8 +1,9 @@ 'use client'; import * as React from 'react'; +import type { DefaultTheme } from '../defaultTheme'; import ThemeContext from './ThemeContext'; -export default function useTheme() { +export default function useTheme(): T { const theme = React.useContext(ThemeContext); if (process.env.NODE_ENV !== 'production') { diff --git a/packages/mui-private-theming/tsconfig.build.json b/packages/mui-private-theming/tsconfig.build.json new file mode 100644 index 00000000000000..b2c7a7f0d20156 --- /dev/null +++ b/packages/mui-private-theming/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + // This config is for emitting declarations (.d.ts) only + // Actual .ts source files are transpiled via babel + "extends": "./tsconfig.json", + "compilerOptions": { + "composite": true, + "declaration": true, + "noEmit": false, + "emitDeclarationOnly": true, + "outDir": "build", + "rootDir": "./src", + "stripInternal": true, + "types": ["@mui/internal-code-infra/build-env", "react"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["src/**/*.test.ts*", "src/**/*.spec.ts*"], + "references": [{ "path": "../mui-utils/tsconfig.build.json" }] +} diff --git a/packages/mui-private-theming/tsconfig.json b/packages/mui-private-theming/tsconfig.json index 52d43eaaa9b975..fc9e91137dd7f5 100644 --- a/packages/mui-private-theming/tsconfig.json +++ b/packages/mui-private-theming/tsconfig.json @@ -1,4 +1,9 @@ { "extends": "../../tsconfig.json", + "compilerOptions": { + "allowJs": false, + "skipLibCheck": true, + "types": ["react", "vitest/globals", "@mui/internal-code-infra/build-env"] + }, "include": ["src/**/*"] } diff --git a/packages/mui-system/tsconfig.build.json b/packages/mui-system/tsconfig.build.json index b143a0736570c0..69886e5cd0eab8 100644 --- a/packages/mui-system/tsconfig.build.json +++ b/packages/mui-system/tsconfig.build.json @@ -13,6 +13,7 @@ "include": ["src/**/*.ts*"], "exclude": ["src/**/*.spec.ts*", "src/**/*.test.ts*"], "references": [ + { "path": "../mui-private-theming/tsconfig.build.json" }, { "path": "../mui-styled-engine/tsconfig.build.json" }, { "path": "../mui-utils/tsconfig.build.json" } ] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8fbfc6134750b6..1a1983924fe0be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1270,6 +1270,9 @@ importers: '@types/chai': specifier: 5.2.3 version: 5.2.3 + '@types/prop-types': + specifier: 15.7.15 + version: 15.7.15 '@types/react': specifier: 19.2.14 version: 19.2.14 From 4b18564156c1bf81c6dd6ebcb54cd1c0e6683357 Mon Sep 17 00:00:00 2001 From: Janpot <2109932+Janpot@users.noreply.github.com> Date: Tue, 26 May 2026 16:42:38 +0200 Subject: [PATCH 2/2] Drop redundant /* remove-proptypes */ on inner exactProp The assignment is inside if (NODE_ENV !== 'production') so it's already dead-code-eliminated in production builds; the marker is unnecessary and inconsistent with the mui-material convention. --- .../mui-private-theming/src/ThemeProvider/ThemeProvider.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx index 8bccbe66f045e5..f503d14cc84ec7 100644 --- a/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx +++ b/packages/mui-private-theming/src/ThemeProvider/ThemeProvider.tsx @@ -85,9 +85,7 @@ function ThemeProvider( }; if (process.env.NODE_ENV !== 'production') { - (ThemeProvider as any).propTypes /* remove-proptypes */ = exactProp( - (ThemeProvider as any).propTypes, - ); + (ThemeProvider as any).propTypes = exactProp((ThemeProvider as any).propTypes); } export default ThemeProvider;