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

时间:2017-12-28 21:52:00

标签: javascript typescript material-ui

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 个答案:

答案 0 :(得分:2)

可以使用module augmentation解决该问题:

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


此外,您可以在App组件中声明该模块,并将子级包装在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>
  )

答案 1 :(得分: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 1}}类型。当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)

但编译器会对隐式空值大喊大叫,这很好,因为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