TypeScript はちゃんと学習していないのですが、関わっているプロダクトが ts, tsx なので触る時に雰囲気で使っています。
今回はあるコンポーネントをラップするコンポーネントを作成していて、バックエンドから渡される値が、ラップするコンポーネントに定義されているPropsと異なっていて、ラップする側で足りない場合はデフォルト値を設定したいという感じでした。
👇こんなイメージ
// ラップするコンポーネントの Props の型 type ModalProps = { modalMode: string; ... returnPath: string; }; // ラップするコンポーネントの Props の型をこうしたい type ModalButtonProps = { label?: string; className?: string; // 以下ラップするコンポーネントの Props modalMode: string; … returnPath?: string; }
ラップするコンポーネントの type を export して returnPath
の type を上書きする感じでマージしたい
type {}
で囲われてるし JS のオブジェクトのマージの感覚で次のように型定義をしたのですが意図したとおりにはなりませんでした。
import { ModalProps } from './Modal'; type ModalButtonProps = ModalProps & { label?: string; className?: string; returnPath?: string; // 上書きしたい type }
後に &
で繋げば、後に書かれている方が優先されるのかな?と思ったのですが、returnPath
が無い Props を受け取ると returnPath: string
だと lint に怒られてしまいました。type ModalButtonProps = {...} & ModalProps
と逆にしてみても同じエラーに…
&
はマージではなく intersection型
チームの人に訊いたら &
はマージではなく intersection という型で「積」だから A & B
も B & A
も同じ結果になると教えてもらいました。これだけでは理解しきれなかったので調べてみた所 intersection というのは「 A かつ B 」という意味ということでした。
(画像で思考するタイプなのでこの図をみてやっと理解できました)
つまり先の例ではで同じキーである returnPath の型は「returnPath: string; かつ returnPath?: string;」という状態を指し、両方を満たすケースは 「returnPath: string;」の時だけとなるので、returnPath
を渡さないとこの条件に合わないのでエラーとなっていた。という事のようでした!
完 全 理 解 !
Omit で 特定の型を取り除く
Omit
type Omit<T, K extends keyof any>
型 T の中から、キー名が K に当てはまるプロパティを除外した新しい型を返します。なお、Pick とは異なり K には T のキー名以外を指定することができます。
cf. TypeScript特有の組み込み型関数 - log.pocka.io / Announcing TypeScript 3.5 | TypeScript
今回は元々ある A: string
を A?:string
にしたい。他にも型を追加したいという感じだったので、Omit を使って元の型から A: string
を除き &
でくっつけるというのが良さそうです。
型の指定を次のように変更したら意図したとおりになりました
// Modal.tsx export type ModalProps = { modalMode: string; returnPath: string; }; // Button.tsx import { ModalProps } from './Modal'; type ModalButtonProps = Omit<ModalProps, 'returnPath'> & { label?: string; className?: string; returnPath?: string; }
ModalButtonProps
は次のような型として解釈されます
type ModalButtonProps = { modalMode: string; label?: string; className?: string; returnPath?: string; }
タイトルの マージ というのとは少し違いますが、今回はこの方法でマージっぽく型をつくる事ができました。
感想
学習時間を取れずプロダクトにあるコードを実践で見様見真似で触っていたので、&
や |
の意味を勘違いしていたことがわかり、その意味を調べて知ることができてよかったです。
が、他にも type
と interface
や色々な指定方法があって雰囲気で使うには JavaScript でどう作れば良いのかは分かってるけど、TypeScript にしたらプロダクトの本質ではない型宣言が知識不足でハマってしまい時間がかかってしまう状態というのが非常にストレスなので、そろそろちゃんと学習せねばという気になりかけています。自分の能力不足だと認識している問題ははストレス大きいですからね…
[参考]