如何在TypeScript中创建支持嵌套修饰符的CSS主题递归对象类型?
问题分析
你遇到的核心问题是:原有的递归Theme类型因为React.CSSProperties自带的字符串索引签名(支持自定义CSS属性),导致深层的非法修饰符会被误判为合法的CSS属性,从而不会触发类型错误。只有第一层的修饰符会被映射类型校验,深层则因为索引签名的“兜底”作用绕过了检查。
解决方案
要实现所有层级都严格校验修饰符合法性,我们需要明确区分合法CSS属性和允许的修饰符,避免索引签名干扰。具体步骤如下:
定义所有允许的修饰符
首先把你需要的所有修饰符(组件名、变体、层级修饰符等)都纳入Modifiers联合类型:type Modifiers = 'button' | 'dark' | 'light' | 'hollow' | 'modfierLevel4' | 'modfierLevel5' | 'modfierLevel6';创建排除修饰符的CSS属性类型
我们需要从React.CSSProperties中排除掉所有属于修饰符的键,避免和修饰符命名冲突(即使当前没有冲突,这样做也更严谨):type CSSPropsWithoutModifiers = Omit<React.CSSProperties, Modifiers>;定义递归的Theme类型
最后,将处理后的CSS属性类型与修饰符的映射类型交叉,形成递归的Theme类型:type Theme = CSSPropsWithoutModifiers & { [K in Modifiers]?: Theme; };
效果验证
现在你在任何层级使用非法修饰符(不在Modifiers中且不是合法CSS属性)都会触发TypeScript错误:
// ❌ 错误:'lgt'不是合法修饰符也不是CSS属性 const invalidTheme: Theme = { button: { lgt: { background: 'red' } } }; // ✅ 合法:所有修饰符都在Modifiers中,CSS属性也合法 const validTheme: Theme = { background: "red", color: "white", button: { background: "red", color: "white", light: { background: "red", color: "white", hollow: { borderColor: "red", color: "red", background: "transparent", } }, dark: { background: "red", color: "white", hollow: { borderColor: "black", color: "black", background: "transparent", modfierLevel4: { "--some": "css", // 自定义CSS变量用--前缀,符合CSS规范 modfierLevel5: { "--some": "css", modfierLevel6: { "--some": "css" } } } } } } };
为什么原方案失效?
原方案type Theme = React.CSSProperties & { [keys in Modifiers]?: Theme }中,React.CSSProperties包含[key: string]: string | number | ...的字符串索引签名,这意味着任何字符串键都会被视为合法的CSS属性(比如自定义变量)。所以深层的非法修饰符会被这个索引签名“接纳”,不会触发错误。而新方案通过Omit明确排除了修饰符作为CSS属性的可能,同时限制了只能使用Modifiers中的键作为嵌套修饰符,从而实现了全层级的严格校验。
内容的提问来源于stack exchange,提问作者Ram Babu

