はじめに
Next.jsのプロジェクトを実装していた時のこと。
ちょっとしたコンポーネントを作りたくて、mui
を使って実装しようとしたら引っかかってしまったところのお話
引っかかった点
引数によってサイズの変わるButton
コンポーネントが作りたかった。
import { Button, Typography } from "@mui/material";
import Image from "next/image";
type HogeButtonProps = {
text: string;
size: "small" | "medium" | "large";
};
const size2int = {
small: 24,
medium: 36,
large: 38,
};
const size2captionSize =
{
small: "h6",
medium: "h5",
large: "h4",
};
export const HogeButton = ({ size, text }: HogeButtonProps) => {
return (
<Button>
<Image
src="/hoge.svg"
width={size2int[size]}
height={size2int[size]}
alt="hoge"
/>
<Typography ml={2} variant={size2captionSize[size]}>
text
</Typography>
</Button>
);
};
HogeButton
には引数としてtext
とsize
が渡せる。
text
はボタンの文字にsize
はボタンの文字の大きさ、画像の大きさに関係する引数である。(割愛しているが実際はもう少し複雑な実装)
この時Typography
コンポーネントのvariant
に以下のエラーが出ていた
Type 'string' is not assignable to type '"h6" | "h5" | "h4" | "button" |
"caption" | "h1" | "h2" | "h3" | "inherit" | "subtitle1" | "subtitle2" | "body1"
| "body2" | "overline" | undefined'.
Overload 2 of 2, '(props: DefaultComponentProps<TypographyTypeMap<{},
"span">>): Element | null', gave the following error.
Type 'string' is not assignable to type '"h6" | "h5" | "h4" | "button" |
"caption" | "h1" | "h2" | "h3" | "inherit" | "subtitle1" | "subtitle2" | "body1"
| "body2" | "overline" | undefined'.ts(2769)
ようはvariant
に入れる値の型が違うよっていう話である。
size2captionSize
の返り値はstring
である。
ので一見大丈夫なように見えるが、mui
のTypography
のvariant
が要求するのはh1
h2
h3
...
といったある一定の文字しか許さない型なのである。
解決策
じゃあどうするのか、size2captionSize
のvalue
をmui
のTypography
のvariant
に一致するようにすればいい。
といってもそんなやり方など私は知らない。
サバイバルTypeScriptを探してやっと見つけた型Record
である。
Record
とは
RecordはプロパティのキーがKeysであり、プロパティの値がTypeであるオブジェクト型を作るユーティリティ型です。
つまりobject
のkey
とvalue
に型がつけれるよという型です。
今回ほしいものにぴったりでした。
てことでいかが解決したコードです。
import {
Button,
Typography,
+ TypographyTypeMap
} from "@mui/material";
import Image from "next/image";
type HogeButtonProps = {
text: string;
size: "small" | "medium" | "large";
};
const size2int = {
small: 24,
medium: 36,
large: 38,
};
+const size2captionSize: Record<string, TypographyTypeMap["props"]["variant"]> =
{
small: "h6",
medium: "h5",
large: "h4",
};
export const HogeButton = ({ size, text }: HogeButtonProps) => {
return (
<Button>
<Image
src="/hoge.svg"
width={size2int[size]}
height={size2int[size]}
alt="hoge"
/>
<Typography ml={2} variant={size2captionSize[size]}>
text
</Typography>
</Button>
);
};
TypographyTypeMap["props"]["variant"]
というのはmui
のTypography
のvariant
の型です。
これでsize2captionSize
はkey
にstring
をvalue
にypographyTypeMap["props"]["variant"]
をとるオブジェクトとなりました。
エラーも出なくなりました。