Material UI の PaletteColor の型をいい感じに拡張させる
半年前から React を触るようになり、Material UIも触るようになりました。
とても便利ですね!
UIフレームワークはこれまで Vue で Vuetify をつかってきましたが、断然 Material UI のほうが拡張性があり、使いやすくて結構好きになりました。
ことの発端
デザイナーからの色指定で primary
カラーのバリエーションに light
main
dark
以外の色を追加したいというユースケースが発生しました。仮に disabled
とします。
具体的なコードを書くと、こんな感じにしたい。
<Box bgcolor="primary.disabled">not available</Box>
JavaScript なら createTheme
のところで下記のようにかけば良いのですが、 TypeScript の場合は ThemeOptions
と型が合わずにエラーとなります。
export const theme = createTheme({
// 略
palette: {
primary: {
light: '#ffe8d6',
main: '#a5a58d',
dark: '#6b705c',
disabled: '#b7b7a4'
// ↑ JS なら通るし Material UI の 色指定でアクセスできる
// TS なら createTheme の時点でエラー。
// 乱暴に @ts-ignore すればとりあえず大丈夫だが
// theme を import したときにメンバーに出てこないのはイケてない
}
}
// 略
});
上記のコメントでかいたとおり、 Material UI の theme 関係の恩恵が受けられませんが、メンバーを勝手に生やしてアクセスすること自体はできているにも関わらず、TSのコンパイルエラーになります。
なので、自前で型を変えちゃいます。
declare module
を使って上書き
Vue の 2系でよくやるやつですね(遠い目)。
いきなりですが、こんな感じの d.ts
を書きました。
import { Theme } from '@material-ui/core';
import { PaletteColor } from '@material-ui/core/styles/createPalette';
declare module '@material-ui/core/styles/createPalette' {
interface CustomTheme extends Theme {
palette: CustomPalette;
}
interface CustomPalette extends Palette {
primary: CustomPaletteColor;
}
interface CustomPaletteColor extends PaletteColor {
disabled?: string;
}
interface SimplePaletteColorOptions {
disabled?: string;
}
}
SimplePaletteColorOptions
ここで background を指定することで、 createTheme
の palette.primary 内に disabled
が使えるようになりました。
CustomPaletteColor
一方、 SimplePaletteColorOptions
だけを拡張しても、 theme を import してもメンバーに disabled
が生えません。なので、 styled components で色を引っ張りたい場合に問題が出ます
import React from 'react';
import { theme } from './theme.ts';
const hoge = (): JSX.Element => (
<p style={{
textDecoration: `underline overline ${theme.palette.primary.disabled}`
// ~~~~~~~~ そんなものはない
}}>hoge</p>
);
export default hoge;
なので、悩んだのですが、 既存の PaletteColor
を拡張して使うことにします。
既に存在するメンバーを拡張させるのは d.ts
を使ってもできないので、新しい interface を定義します。今回作成した CustomPaletteColor
がそれです。
CustomPalette
上記と同じ理由です。今回は primary
の PaletteColor だけを拡張させたいので、 primary
だけ拡張した CustomPaletteColor
を使いましたが、他の色 error
とかも拡張させたい場合は、そちらも追記してください。
CustomTheme
これも上記と同じ & 本丸です。これを新しい Theme の型として使いたいのですが、悩んだ結果、 theme
に対して アサーションで読み替えてあげることにしました。
export const theme = createTheme({ /* 設定値 */ }) as CustomTheme;
実行結果
import React from 'react';
import { theme } from './theme.ts';
const hoge = (): JSX.Element => (
<p style={{
textDecoration: `underline overline ${theme.palette.primary.disabled}`
// ↑メンバーが生えた
}}>hoge</p>
);
export default hoge;
これでいけそうです
もちろん、Material UIのコンポーネントでも指定できます。(こっちは文字列渡しなので型の恩恵は受けられませんが…)
<Box bgcolor="primary.disabled">not available</Box>
ところで
実は当初、型を眺めていたところ、色の指定で利用されている型 PaletteColorOptions
では ColorPartial
が union で指定されていて、普通に使えそうなんです。
// @material-ui/core/index.d.ts から抜粋
export interface Color {
50: string;
100: string;
200: string;
300: string;
400: string;
500: string;
600: string;
700: string;
800: string;
900: string;
A100: string;
A200: string;
A400: string;
A700: string;
}
// @material-ui/core/styles/createPalette.d.ts から抜粋
export type ColorPartial = Partial<Color>;
export type PaletteColorOptions = SimplePaletteColorOptions | ColorPartial;
export interface SimplePaletteColorOptions {
light?: string;
main: string;
dark?: string;
contrastText?: string;
}
ところが、実際に生える theme インスタンスの palette の中身の型は PaletteColor
になっています。
// @material-ui/core/styles/createPalette.d.ts から抜粋
export interface PaletteColor {
light: string;
main: string;
dark: string;
contrastText: string;
}
同じ問題に気づいた方がいらっしゃたようで、こちらの Issue でディスカッションとなっていました。
https://github.com/mui-org/material-ui/issues/20277
で、このIssueを読んで、今回の対処法に行き着いた次第です。
まとめ
v5 ではもうちょっとかんたんに拡張できそうですね。
それでは。
Author And Source
この問題について(Material UI の PaletteColor の型をいい感じに拡張させる), 我々は、より多くの情報をここで見つけました https://qiita.com/akagire/items/b6e32e7dcf9ef124b365著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .