使用material-ui@next和typescript扩展主题

4

在为 material-ui 创建我的主题时,我添加了两个新的调色板选项,使我拥有更好的浅色和深色范围。我已经扩展了 Theme 类型来指示此操作。

import {Theme} from "material-ui/styles";
import {Palette} from "material-ui/styles/createPalette";

export interface ExtendedTheme extends Theme {
    palette: ExtendedPalette
}

export interface ExtendedPalette extends Palette {
    light: Color,
    dark: Color,
}
当我尝试在WithStyles渲染助手中使用这些附加选项时,会出现问题。
const styles = (theme : ExtendedTheme) => ({ root: {color: theme.light['100'] }});

export interface MyProps {classes: {[index: string] : string}};
const MyComponent = (props : MyProps) => {...};

// Type ExtendedTheme is not assignable to Theme
export default withStyles(styles : StyleRulesCallback)(MyComponent);

从功能上讲,我的纯JavaScript代码可以正常工作,但由于类型不同,它会抛出错误。material-ui的类型定义期望样式回调函数的唯一参数是Theme类型:

export type StyleRulesCallback<ClassKey extends string = string> = (theme: Theme) => StyleRules<ClassKey>;

我认为通过扩展接口的方式可以实现多态,使ExtendedTheme实现Theme

2个回答

3

可以使用模块增强来解决这个问题:

declare module '@material-ui/core' {
  interface Theme {
    colors: {
      success: {
        dark: string,
        light: string,
      }
    }
  }
}


此外,您可以在应用程序组件中声明该模块,并将子元素包装在ThemeProvider中:
import { createMuiTheme, ThemeProvider, colors, ThemeOptions } from '@material-ui/core';

declare module '@material-ui/core' {
  interface Theme {
    colors: {
      success: {
        dark: string,
        light: string,
      }
    }
  }
}

const App = () => {
  const theme = createMuiTheme({
    colors: {
      success: {
        dark: colors.green[600],
        light: colors.green[300],
      },
    } as ThemeOptions,
  });

  return (
    <ThemeProvider theme={theme}>
     <a href="https://material-ui.com/customization/theming/">Theming</a>
    </ThemeProvider>
  )

请注意,在声明模块时,您应该导入您正在扩展的接口。例如:import { Theme } from "@material-ui/core/styles/createMuiTheme"; - thisismydesign

1
我想到的唯一解决办法是将我的自定义选项设置为可选,如下所示。
export interface ExtendedPalette extends Palette {
    light?: Color,
    dark?: Color,
}

然后在我的样式回调中,我必须检查这些选项是否存在,这有点麻烦,但我认为没有其他解决方法。

const styles = (theme : ExtendedTheme) => { 
    let light = theme.palette.light[100];
    if(light === undefined) light = theme.common.white;
    { root: {color: light }}
};
这是因为使用 withStyles 时,回调函数会传递 Theme 对象,但回调函数的类型定义使用 Theme 类型,因为它们无法了解我的 ExtendedTheme 类型。冲突在于 ExtendedTheme 必须具有 Theme 不知道的选项。通过使这些额外选项可选,Theme 仍然可以符合 ExtendedTheme。基本上,扩展接口可以传递其父级所期望的位置,但其父级不能传递到期望扩展接口的位置,除非以一种使父级仍然符合的方式扩展扩展接口。 一个更简单的示例很有启发性。
export interface Foo {foo: string};
export interface Bar extends Foo {bar: string}

function getFoo(f : Foo) {console.log(f.foo)}
function getBar(b : Bar) {console.log(b.bar)} 
function getFooBar(fb: Bar) {console.log(fb.foo, fb.bar)}

const f : Foo = {foo: 'foo'}
const b : Bar = {foo: 'foo', bar: 'bar'}

getFoo(f) // foo
getFoo(b) // foo
getBar(f) // Error Incompatible Type
getBar(b) // bar
getFooBar(f) // Error Incompatible Type
getFooBar(b) // foo bar
"

getFoo(b) 的工作原理是因为 Bar 至少拥有 Foo 拥有的一切。 getBar(f)getFooBar(f) 都会失败,因为编译器看到类型 Foo 没有键 bar

通过这样重新定义 Bar

"
export interface Bar extends Foo {bar? : string}
编译器现在知道Foo符合Bar类型的最低要求,但您需要检查是否有隐式null。因此,这将起作用。
getBar(f)

但编译器会报告隐式的 null 值,这是好的,因为 f.bar 是未定义的。所以你必须重新定义你的函数如下:

function getBar(b : Bar) {
    let bar = b.bar
    if(bar === undefined) bar = b.foo;
    console.log(bar);
}

getBar(b) // bar
getBar(f) // foo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,