Skip to content

[code-infra] Convert @mui/styled-engine-sc to TypeScript#48577

Merged
Janpot merged 3 commits into
mui:masterfrom
Janpot:code-infra/styled-engine-sc-typescript
May 28, 2026
Merged

[code-infra] Convert @mui/styled-engine-sc to TypeScript#48577
Janpot merged 3 commits into
mui:masterfrom
Janpot:code-infra/styled-engine-sc-typescript

Conversation

@Janpot
Copy link
Copy Markdown
Member

@Janpot Janpot commented May 27, 2026

Converts @mui/styled-engine-sc to true TypeScript (same setup as @mui/utils/@mui/styled-engine/@mui/private-theming): hand-written .js + .d.ts source replaced with .ts/.tsx, declarations now emitted by tsc, --skipTsc dropped, tsconfig.build.json added.

The hand-written .d.ts over-claimed the type surface by re-exporting the entire styled-components named-export set (export * from 'styled-components') while the runtime .js only re-exported ThemeContext, keyframes, css. To keep the exported type surface 100% identical (verified via a bidirectional type-equivalence probe over the root and every subpath entry), the new src/index.ts keeps export * from 'styled-components', so the runtime JS now also re-exports the broader surface — fixing the prior type/runtime divergence rather than narrowing the types to match the runtime. The local default stays the dev-mode wrapper, cast to typeof scStyled so its public type matches the hand .d.ts's export { default } from 'styled-components'.

Other .js deltas are limited to standard true-TS conversion artifacts: propTypes assignments now wrap in process.env.NODE_ENV !== 'production' ? … : void 0 via the /* remove-proptypes */ marker, and index's default export form becomes function styled; export default styled; (same runtime). .d.ts differences are type-preserving forms (import type, export type *, declare).

There are no internal MUI workspace consumers of @mui/styled-engine-sc, so no downstream tsconfig.build.json references updates were needed.

Published artifact diff

https://code-infra-dashboard.onrender.com/diff-package?package1=https%3A%2F%2Fpkg.pr.new%2Fmui%2Fmaterial-ui%2F%40mui%2Fstyled-engine-sc%4074a6177&package2=https%3A%2F%2Fpkg.pr.new%2Fmui%2Fmaterial-ui%2F%40mui%2Fstyled-engine-sc%402f907c1

Same setup as @mui/styled-engine (mui#48544) and @mui/private-theming
(mui#48565): hand-written `.js` + `.d.ts` source replaced with a single
`.ts`/`.tsx`, declarations now emitted by `tsc` instead of copied,
`--skipTsc` dropped from the build script, `tsconfig.build.json` added.

This package's hand-written `.d.ts` over-claimed the type surface by
re-exporting the entire `styled-components` named-export set via
`export * from 'styled-components'`, while the runtime `.js` only
re-exported `ThemeContext`, `keyframes`, `css`. To keep the exported
type surface 100% identical (proven via a bidirectional
type-equivalence probe over root + every subpath entry), the new
`src/index.ts` keeps the `export * from 'styled-components'` — so the
runtime JS now also re-exports the broader surface, fixing the prior
type/runtime divergence. The local default stays the dev-mode wrapper,
cast to `typeof scStyled` so its public type matches the hand `.d.ts`'s
`export { default } from 'styled-components'`.

Other JS deltas are limited to the standard true-TS conversion
artifacts: propTypes assignments now wrap in the
`process.env.NODE_ENV !== 'production' ? … : void 0` guard via the
`/* remove-proptypes */` marker, and `index`'s default export form
becomes `function styled; export default styled;` (same runtime).
`.d.ts` differences are limited to type-preserving forms (`import type`,
`export type *`, `declare`, expando-free default emit).

There are no internal MUI workspace consumers of `@mui/styled-engine-sc`,
so no downstream `tsconfig.build.json` `references` updates were needed.

## Published artifact diff

Diff of the published package before/after this PR: [`@mui/styled-engine-sc`].
@code-infra-dashboard
Copy link
Copy Markdown

code-infra-dashboard Bot commented May 27, 2026

Deploy preview

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

Bundle size

Bundle Parsed size Gzip size
@mui/material 0B(0.00%) 0B(0.00%)
@mui/lab 0B(0.00%) 0B(0.00%)
@mui/private-theming 0B(0.00%) 0B(0.00%)
@mui/system 0B(0.00%) 0B(0.00%)
@mui/utils 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@Janpot Janpot added the scope: code-infra Involves the code-infra product (https://www.notion.so/mui-org/5562c14178aa42af97bc1fa5114000cd). label May 27, 2026
Janpot added 2 commits May 27, 2026 13:37
The TS conversion previously kept `export * from 'styled-components'` to
preserve the over-claimed surface of the hand-written `.d.ts`, which
broadened the published JS to re-export the entire styled-components
surface (`ServerStyleSheet`, `ThemeProvider`, `StyleSheetManager`, …).

The hand-written `.d.ts` was a long-standing bug — those symbols
resolved to `undefined` at runtime, so no consumer can have been
relying on them. The sibling `@mui/styled-engine` (emotion) deliberately
does not `export *` either. Narrow the type surface to match the
original narrow runtime instead of the other way around: drop the
broad re-export and put back the explicit `ThemeContext, keyframes, css`
named re-export from the old `index.js`.
Bring back the type half of the original hand-written `.d.ts` via
`export type * from 'styled-components'`. The original split was:
broad types (via `export *` and `export { default } from
'styled-components'` in the `.d.ts`), narrow runtime (only
`ThemeContext, keyframes, css` re-exported in the `.js`). Use a
type-only re-export so the broad surface stays on the type side and
emits nothing at runtime. Local type declarations still shadow the
styled-components ones, as before.
@Janpot Janpot marked this pull request as ready for review May 27, 2026 13:12
@Janpot Janpot requested review from a team May 27, 2026 14:39
import PropTypes from 'prop-types';
import { createGlobalStyle, type CSSObject, type StyleFunction } from 'styled-components';

export interface GlobalStylesProps<Theme extends object = {}> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
export interface GlobalStylesProps<Theme extends object = {}> {
export interface GlobalStylesProps<Theme extends object = Record<string, any>> {

Is this more accurate? {} could be primitive.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Maybe, but I'm not planning on changing types in this PR unless strictly necessary for the migration. The goal is migrating to typescript with no impact on exported javascript, and the least possible impact to the exported types. Essentially only fixing bugs in the old handwritten declaration files that can't be expressed exactly in a .ts => .d.ts transformation.

Copy link
Copy Markdown
Member

@siriwatknp siriwatknp left a comment

Choose a reason for hiding this comment

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

LGTM, nit on the {}.


export default GlobalStyles;

(GlobalStyles as any).propTypes /* remove-proptypes */ = {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I remember from some other code (not sure where) that we also wrap propTypes behind NODE_ENV !== 'production' check.

Copy link
Copy Markdown
Member Author

@Janpot Janpot May 28, 2026

Choose a reason for hiding this comment

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

/* remove-proptypes */ causes this in the build output

-GlobalStyles.propTypes = {
+process.env.NODE_ENV !== "production" ? GlobalStyles.propTypes /* remove-proptypes */ = {
   defaultTheme: _propTypes.default.object,
   styles: _propTypes.default.oneOfType([_propTypes.default.array, _propTypes.default.string, _propTypes.default.object, _propTypes.default.func])
-};
+} : void 0;

@Janpot Janpot merged commit 4a664f4 into mui:master May 28, 2026
19 checks passed
@Janpot
Copy link
Copy Markdown
Member Author

Janpot commented Jun 2, 2026

Part of the ts-package-migration umbrella: mui/mui-public#1510

@oliviertassinari oliviertassinari added the type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. label Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: code-infra Involves the code-infra product (https://www.notion.so/mui-org/5562c14178aa42af97bc1fa5114000cd). type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. typescript

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants