如何为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
相关产品推荐
相关产品推荐

