typescript 型のお話 Record

# はじめに
Next.jsのプロジェクトを実装していた時のこと。
ちょっとしたコンポーネントを作りたくて、`mui`を使って実装しようとしたら引っかかってしまったところのお話

# 引っかかった点

引数によってサイズの変わる`Button`コンポーネントが作りたかった。
```tsx:hoge.ts
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](https://typescriptbook.jp/)を探してやっと見つけた型`Record`である。

`Record`とは

>Record<Keys, Type>はプロパティのキーがKeysであり、プロパティの値がTypeであるオブジェクト型を作るユーティリティ型です。

引用:[サバイバルTypescript Record](https://typescriptbook.jp/reference/type-reuse/utility-types/record)


つまり`object`の`key`と`value`に型がつけれるよという型です。
今回ほしいものにぴったりでした。


てことでいかが解決したコードです。
```diff_tsx:hoge.ts
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"]`をとるオブジェクトとなりました。
エラーも出なくなりました。