はじめに
通常でもpropsに型を指定して、記述できる内容を制限していますが、
状況により、制限する内容を変えたい場合、ジェネリクスで指定できれば見た目にも分かりやすくなります
今回作るもの
今回の要件として、リンクのコンポーネントを用意します
リンクの型を作成
// 基本
interface BaseLinkType {
path: `https://${string}` | `/${string}`
text: string
}
// IR
export interface Ir extends BaseLinkType {
path: `/ir/${string}`
}
// お知らせ
export interface Information extends BaseLinkType {
path: `/info/${string}`;
text: `お知らせ:${string}`
}
// サービスサイト
export interface Service extends BaseLinkType {
path: `https://www.xxxx.co.jp/${string}`
}
コンポーネントにジェネリクスの型を適用する
import React, { PropsWithChildren } from 'react';
type Link = Ir | Information | Service
interface Props<T extends Link> {
path: T['path']
text: T['text']
}
export const Link = <T extends Link>({
path,
text
}: PropsWithChildren<Props<T>>): React.ReactElement => {
return (
<a href={path}>{text}&l;/a>
)
}
これで、ジェネリクスを指定したLinkコンポーネントは完成です
Linkコンポーネントを利用する側の実装例です
import React from 'react'
import { Link, type Information } from './Link'
export const Test: React.FC = () => {
return (
<Link<Information>
path="/info/20230412"
text="お知らせ:XX店オープン"
/>
)
}
リファクタリング
ジェネリクスの指定を、文字列にした方が、型を export / import しなくてよくなりますtype Link = {
ir: Ir
info: Information
service: Service
}
interface Props<T extends keyof Link> {
path: Link[T]['path']
text: Link[T]['text']
}
export const Link = <T extends keyof Link>({
path,
text
}: PropsWithChildren<Props<T>>): React.ReactElement => {
return (
<a href={path}>{text}</a>
)
}
このようにすると、各Linkの型のexportも不要になり、利用する側も少しですが記述する量が少なくなります
import React from 'react'
import { Link } from './Link'
export const Test: React.FC = () => {
return (
<Link<'info'>
path="/info/20230412"
text="お知らせ:XX店オープン"
/>
)
}
リファクタリング後のコンポーネント全体のコード
import React, { PropsWithChildren } from 'react';
// 基本
interface BaseLinkType {
path: `https://${string}` | `/${string}`;
text: string;
}
// IR
interface Ir extends BaseLinkType {
path: `/ir/${string}`;
}
// お知らせ
interface Information extends BaseLinkType {
path: `/info/${string}`;
text: `お知らせ:${string}`
}
// サービスサイト
interface Service extends BaseLinkType {
path: `https://www.xxxx.co.jp/${string}`;
}
type Link = {
ir: Ir
info: Information
service: Service
}
interface Props<T extends keyof Link> {
path: Link[T]['path']
text: Link[T]['text']
}
export const Link = <T extends keyof Link>({
path,
text
}: PropsWithChildren<Props<T>>): React.ReactElement => {
return (
<a href={path}>{text}</a>
)
}