You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为MUI的makeStyles正确定义类型,避免重复指定Props与类名泛型

为MUI废弃的makeStyles自动推导Props与类名类型

我清楚MUI的makeStyles已经是废弃的遗留API,但仍想解决类型定义的问题:既要避免在每个样式回调里重复声明Props类型,又要让生成的useStyles自动识别定义的类名(比如"mainContainer" | "subContainer"),而非默认的ClassNameMap<string>,同时不想手动传入第三个泛型参数。

当前可行但繁琐的写法

每次写样式回调都要手动指定ThemeProps:

import { createTheme } from '@mui/material';
import { makeStyles } from '@mui/styles';

interface ThemeProps {
  alignItems: boolean;
  themeVersion: 'primary' | 'secondary'
}

const appTheme = createTheme();

const useStyles = makeStyles((theme: typeof appTheme) => {
  return {
    mainContainer: ({ alignItems }: ThemeProps) => ({
      alignItems: alignItems ? 'center' : '',
      height: '100%',
      padding: theme.spacing(2),
    }),
    subContainer: {
      display: 'flex',
      justifyContent: 'center',
      backgroundColor: ({ themeVersion }: ThemeProps) =>
        themeVersion === 'secondary'
          ? theme.palette.secondary.main
          : theme.palette.primary.main,
    },
  };
});

期望的写法(但返回类型不符合预期)

提前指定泛型后,不用重复声明Props,但useStyles返回类型变成了ClassNameMap<string>:

const useStyles = makeStyles<typeof appTheme, ThemeProps>(theme => {
  return {
    mainContainer: ({ alignItems }) => ({
      alignItems: alignItems ? 'center' : '',
      height: '100%',
      padding: theme.spacing(2),
    }),
    subContainer: {
      display: 'flex',
      justifyContent: 'center',
      backgroundColor: ({ themeVersion }) =>
        themeVersion === 'secondary'
          ? theme.palette.secondary.main
          : theme.palette.primary.main,
    },
  };
});

解决方案

方案1:先定义样式规则,让TypeScript自动推导键类型

把样式逻辑抽成单独的常量,让TS自动识别类名的联合类型,再传入makeStyles:

import { createTheme } from '@mui/material';
import { makeStyles, StyleRules } from '@mui/styles';

interface ThemeProps {
  alignItems: boolean;
  themeVersion: 'primary' | 'secondary'
}

const appTheme = createTheme();
type AppTheme = typeof appTheme;

// 先定义样式规则,TS会自动推断出键类型为"mainContainer" | "subContainer"
const styles = (theme: AppTheme): StyleRules<ThemeProps> => ({
  mainContainer: ({ alignItems }) => ({
    alignItems: alignItems ? 'center' : '',
    height: '100%',
    padding: theme.spacing(2),
  }),
  subContainer: {
    display: 'flex',
    justifyContent: 'center',
    backgroundColor: ({ themeVersion }) =>
      themeVersion === 'secondary'
        ? theme.palette.secondary.main
        : theme.palette.primary.main,
  },
});

// 此时TS会自动填充第三个泛型参数,useStyles返回类型为ClassNameMap<"mainContainer" | "subContainer">
const useStyles = makeStyles<AppTheme, ThemeProps>(styles);

方案2:封装自定义工具函数,简化重复操作

如果频繁用到这种场景,可以封装一个工具函数,自动帮我们完成所有泛型的推导:

import { createTheme } from '@mui/material';
import { makeStyles, StyleRules } from '@mui/styles';

interface ThemeProps {
  alignItems: boolean;
  themeVersion: 'primary' | 'secondary'
}

const appTheme = createTheme();
type AppTheme = typeof appTheme;

// 封装工具函数,自动推导Theme、Props和样式键类型
const createUseStyles = <Props, Theme = AppTheme>(
  styles: (theme: Theme) => StyleRules<Props>
) => makeStyles<Theme, Props>(styles);

// 使用时无需手动指定任何泛型,TS会自动识别所有类型
const useStyles = createUseStyles((theme) => ({
  mainContainer: ({ alignItems }) => ({
    alignItems: alignItems ? 'center' : '',
    height: '100%',
    padding: theme.spacing(2),
  }),
  subContainer: {
    display: 'flex',
    justifyContent: 'center',
    backgroundColor: ({ themeVersion }) =>
      themeVersion === 'secondary'
        ? theme.palette.secondary.main
        : theme.palette.primary.main,
  },
}));

原理说明

makeStyles的第三个泛型参数ClassKey默认值是string,当手动指定前两个泛型时,TS无法自动推导第三个。通过先定义样式规则让TS识别键类型,或者封装工具函数,就能让TS自动填充第三个泛型,从而得到精确的类名联合类型。

内容的提问来源于stack exchange,提问作者DanielYoshua

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:01:04