😁
TypeScriptにおけるコンポーネント間の引数型厳密比較について、解決策を調査する
はじめに
親コンポーネントから子コンポーネントへスプリット構文を利用して引数を渡すと、不要なプロパティを許容するので困っている。
解決策がないか調査する。
ESLintのReact/jsx-props-no-spreadingを有効にする。という選択肢は、一度脇に置いておきます。
試しに
type.ts
export type Exact<T, U extends T = T> = T & {
[K in keyof U]: K extends keyof T ? T[K] : never;
};
export type DataProps = {
title: string;
description: string;
sercretInfo: string;
};
export type DataPropsWithCC = Exact<
Omit<DataProps, "sercretInfo">
>;
RSC.tsx
import CC from "./CC";
import { DataProps } from "./type";
const getData = async () => {
// 任意のデータ取得ロジック
const data: DataProps = {
title: "title",
description: "description",
sercretInfo: "sercretInfo",
};
return data;
};
const RSC = async () => {
const data = await getData();
const { sercretInfo, ...restData } = data;
return (
<>
{/* 型エラーが起こらないでほしい */}
<CC
title={restData.title}
description={restData.description}
/>
{/* 型エラーが起こってほしい */}
<CC {...data} />
{/* 型エラーが起こらないでほしい */}
<CC {...restData} />
</>
);
};
export default RSC;
CC.tsx
"use client";
import { DataPropsWithCC } from "./type";
const CC = (props: DataPropsWithCC) => {
const { title, description } = props;
return (
<>
<h1>{title}</h1>
<p>{description}</p>
</>
);
};
export default CC;
{/* 型エラーが起こってほしい */}
<CC {...data} />
上記のように型上はsercretInfoが不要なのだが、スプリット構文で引数を渡すと型に許容されてしまう。これを改善したい。
そもそも原因は何か。
TypeScriptの型システムが構造的部分型を採用しているから
ということらしい。恥ずかしながら知らなかった。
解決策については
先人の知識があった。とてもありがたい。
よって、最終的に以下となる。
type
export type StrictPropertyCheck<T, TExpected, TError> = T &
(Exclude<keyof T, keyof TExpected> extends never
? {}
: TError);
export type DataProps = {
title: string;
description: string;
sercretInfo: string;
};
export type DataPropsWithCC = Omit<
DataProps,
"sercretInfo"
>;
RSC
import CC from "./CC";
import { DataProps } from "./type";
const getData = async () => {
// 任意のデータ取得ロジック
const data: DataProps = {
title: "title",
description: "description",
sercretInfo: "sercretInfo",
};
return data;
};
const RSC = async () => {
const data = await getData();
const { sercretInfo, ...restData } = data;
return (
<>
{/* 型エラーが起こらない */}
<CC
title={restData.title}
description={restData.description}
/>
{/* 型エラーが起こる */}
<CC {...data} />
{/* 型エラーが起こらない */}
<CC {...restData} />
</>
);
};
export default RSC;
CC
"use client";
import {
DataPropsWithCC,
StrictPropertyCheck,
} from "./type";
const CC = <T extends DataPropsWithCC>(
props: StrictPropertyCheck<
T,
DataPropsWithCC,
`T has excess property`
>
) => {
const { title, description } = props;
return (
<>
<h1>{title}</h1>
<p>{description}</p>
</>
);
};
export default CC;
終わりに
とてもためになった。
そうか。そうだったのか。
参考文献
Discussion