🙆
[TypeScript] 「このプロパティを渡すならこのプロパティも必須だよ」を実現する Generics
コードから
type MinimalProps = {
prop1: string;
prop2: string;
prop3?: string;
};
type WithMaskProps = MinimalProps & {
maskProp1: string;
maskProp2: string;
maskProp3?: string;
}
type ExcludeUnknownProps<T, U> = {
[K in keyof U as K extends keyof T ? K : never]: K extends keyof T
? T[K]
: never;
} & Partial<{
[K in keyof U as K extends keyof T ? never : K]: never;
}>;
type Props =
| ExcludeUnknownProps<MinimalProps, WithMaskProps>
| WithMaskProps;
↓↓↓
すると、これは OK
const validProps1: Props = {
prop1: 'aaa',
prop2: 'iii',
prop3: 'uuu'
};
const validProps2: Props = {
prop1: 'aaa',
prop2: 'iii',
prop3: 'uuu',
maskProp1: 'eee',
maskProp2: 'ooo'
};
これは Error
const invalidProps: Props = {
prop1: 'aaa',
prop2: 'iii',
prop3: 'uuu',
maskProp1: 'eee'
// maskProp2 がないよ!!
};
イイネ!!
ExcludeUnknownProps
が必要なの?
なぜ ExcludeUnknownProps
を使わない場合、上の invalidProps
はエラーになりません。
type UnexpectedProps = MinimalProps | WithMaskProps;
const invalidProps: UnexpectedProps = {
prop1: 'aaa',
prop2: 'iii',
prop3: 'uuu',
maskProp1: 'eee'
// maskProp2 がないのにエラーにならないよ!!
};
MinimalProps
が暗黙的にインデックスシグネチャを持っているので知らないプロパティを全て受け付けてしまうためです。(ちょっとこの説明自信ない)
ExcludeUnknownProps
は何してるの?
ExcludeUnknownProps
は2つの型を受け取ります。
2つ目の型のプロパティの中で、1つ目の型に存在しないプロパティは never を設定します。
ExcludeUnknownProps<MinimalProps, WithMaskProps>
の場合、 MinimalProps
に存在しないプロパティは渡しちゃダメ、ということになります。
これと組み合わせて ExcludeUnknownProps<MinimalProps, WithMaskProps> | WithMaskProps
とすることで、 MinimalProps
だけの props or WithMaskProps
を全部持つ props のどちらかを受け付けるようになります。
Partial<{
[K in keyof U as K extends keyof T ? never : K]: never;
}>
ここの Partial
ですが、 optional にしないと never 型を渡しなさい、といわれてしまうので Partial
でラップしています。
おわりに
こんなコード自分では書けません。
今日もありがとう、ChatGPT。
Discussion