Skip to content

[AvatarGroup] Fix AvatarGroup typescript performance when using slotProps - #49213

Merged
sai6855 merged 3 commits into
mui:masterfrom
sai6855:avatargrouop-ts-perf
Sep 25, 2026
Merged

sai6855 merged 3 commits into
mui:masterfrom
sai6855:avatargrouop-ts-perf

Conversation

@sai6855

@sai6855 sai6855 commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

The AvatarGroup surplus slot defaults to Avatar, but its props type uses React.ElementType. This unnecessarily checks AvatarProps against every HTML/SVG element. Use typeof Avatar directly, matching the runtime implementation.

Performance Gain

For: <AvatarGroup slotProps={{ surplus: {} }} />

TypeScript instantiations decrease from 39,431 to 15,827 approximately `60% fewer.

I got above number from running tsc --noEmit --extendedDiagnostics

@code-infra-dashboard

code-infra-dashboard Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-49213--material-ui.netlify.app/
QR code for https://deploy-preview-49213--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.

@sai6855
sai6855 requested a balanced review from Copilot September 25, 2026 09:18

This comment was marked as spam.

@sai6855
sai6855 requested a balanced review from Copilot September 25, 2026 09:19

This comment was marked as spam.

@sai6855

sai6855 commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

@claude review

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

PR review

This is a one-line, type-only change to AvatarGroup's surplus slot props, swapping React.ElementType<React.ComponentPropsWithRef<typeof Avatar>> for typeof Avatar. The premise is sound: React.ElementType<P> expands to a mapped conditional over every key of JSX.IntrinsicElements, and SlotComponentProps then distributes ComponentPropsWithRef over that union — so the old form built a large union just to end up with Avatar's props. Nothing merge-blocking: the resulting prop surface is effectively the same (the old union's ComponentType<P> arm already resolved to DefaultComponentProps<AvatarTypeMap>, and the surviving intrinsic arms only contributed HTMLAttributes<HTMLElement>, a subset of div props), and SlotProps<typeof Typography, …> in FormControlLabel.d.ts:21 is existing precedent that an OverridableComponent satisfies the TSlotComponent extends React.ElementType constraint. The remaining issues are a formatting nit that will trip lint and a missing type test.

Tests (1)

1. 🟠 Public slot-props contract changed with no type test

Location: packages/mui-material/src/AvatarGroup/AvatarGroup.spec.tsx:1

<AvatarGroup component="ul" />;
<AvatarGroup variant="circular" />;
// ...nothing exercising slots / slotProps

The PR narrows a public type and the only thing that can catch a regression here is tsc. AvatarGroup.spec.tsx already exists as the type-contract file for this component, but it never touches slotProps.surplus, so neither the old nor the new shape is pinned. In particular, typeof Avatar resolves via the last overload of OverridableComponent (the non-component signature), so the inferred props are DefaultComponentProps<AvatarTypeMap>. If someone later reorders those overloads, or wraps Avatar differently, slotProps.surplus could silently collapse to {} and every prop would still type-check.

Failure scenario: A future refactor of OverridableComponent makes ComponentProps<typeof Avatar> infer {}; slotProps={{ surplus: { notAProp: 1 } }} starts passing type-check and no test fails.

Fix: Add positive and negative assertions to AvatarGroup.spec.tsx, e.g.

<AvatarGroup slotProps={{ surplus: { variant: 'rounded', className: 'x' } }} />;
<AvatarGroup slotProps={{ surplus: (ownerState) => ({ variant: ownerState.variant }) }} />;
// @ts-expect-error
<AvatarGroup slotProps={{ surplus: { nonExistentProp: true } }} />;

Simplifications (2)

1. 🟡 Type argument list should collapse to one line after the change

Location: packages/mui-material/src/AvatarGroup/AvatarGroup.d.ts:25

    surplus: SlotProps<
      typeof Avatar,
      AvatarGroupComponentsPropsOverrides,
      AvatarGroupOwnerState
    >;

The multi-line break was only needed because the old type argument was long. Collapsed, the line is ~98 characters — under the 100-column printWidth this repo uses (compare Dialog.d.ts:51 and mui-system/src/Grid/GridProps.ts:104, both single lines at 99–100 chars). Prettier will reflow it, so pnpm prettier / the lint job will report the file as unformatted.

Failure scenario: CI lint fails on an otherwise-green one-line PR, costing a round trip.

Fix:

    surplus: SlotProps<typeof Avatar, AvatarGroupComponentsPropsOverrides, AvatarGroupOwnerState>;

2. ℹ️ The expensive expansion is only partly removed

Location: packages/mui-material/src/Avatar/Avatar.d.ts:49

    fallback: SlotProps<
      React.ElementType<SvgIconProps>,
      AvatarFallbackSlotPropsOverrides,
      AvatarOwnProps
    >;

typeof Avatar resolves to DefaultComponentProps<AvatarTypeMap>, which includes AvatarSlotsAndSlotProps — and that still contains React.ElementType<SvgIconProps> for the fallback slot. So the JSX.IntrinsicElements sweep is still pulled in transitively whenever AvatarGroup's slotProps type is instantiated; this PR removes one level, not the whole cost. The same pattern is used in Dialog.d.ts:51/56, Menu.d.ts:59/64/69, and StepLabel.d.ts:43.

Failure scenario: The PR's measured improvement is smaller than expected, and the next AvatarGroup type-perf report lands on the same code path.

Fix: Out of scope for this PR, but worth a follow-up applying the same typeof Component substitution to Avatar.fallback and the other React.ElementType<XxxProps> slot types — and worth including a before/after tsc --extendedDiagnostics (or --generateTrace) number in this PR's description so the win is verifiable.

Verdict

Approve after nits — the type substitution is correct and effectively behavior-preserving; only the Prettier reflow and the missing spec.tsx assertions are worth addressing before merge.


🤖 Review generated with Claude Code · Opus 5 (High) · medium review depth · 42 turns · 4m40s · $1.58 · run

@sai6855

sai6855 commented Sep 25, 2026

Copy link
Copy Markdown
Member Author

All issues from claude review is resolved, except for 2nd point. If approach in this PR is fine for reviewers, i'll create same PR for other components

@sai6855
sai6855 marked this pull request as ready for review September 25, 2026 11:21
@sai6855
sai6855 merged commit 346815d into mui:master Sep 25, 2026
19 checks passed
@sai6855 sai6855 changed the title [AvatarGroup] Fix AvatarGroup ts perf when using slotProps Sep 25, 2026
@sai6855 sai6855 added the scope: avatar Changes related to the avatar. label Sep 25, 2026
sai6855 added a commit that referenced this pull request Sep 25, 2026
…49215)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: saichand-fountane <saichand-fountane@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance scope: avatar Changes related to the avatar. typescript

3 participants