はじめに
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"]をとるオブジェクトとなりました。
エラーも出なくなりました。