Przy pomocy Excludetypu, który został dodany w TypeScript 2.8, można uogólnić sposób wymagania co najmniej jednej z zestawu właściwości:
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys]
Częściowym, ale nie absolutnym sposobem wymagania, aby jeden i tylko jeden był zapewniony, jest:
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
Oto link do placu zabaw TypeScript pokazujący oba w akcji.
Zastrzeżenie RequireOnlyOnepolega na tym, że TypeScript nie zawsze zna w czasie kompilacji każdą właściwość, która będzie istniała w czasie wykonywania. Więc oczywiście RequireOnlyOnenie można nic zrobić, aby zapobiec dodatkowym właściwościom, o których nie wie. Podałem przykład tego, jak RequireOnlyOnemożna przeoczyć rzeczy na końcu linku do placu zabaw.
Krótkie omówienie tego, jak to działa na poniższym przykładzie:
interface MenuItem {
title: string;
component?: number;
click?: number;
icon: string;
}
type ClickOrComponent = RequireAtLeastOne<MenuItem, 'click' | 'component'>
Pick<T, Exclude<keyof T, Keys>>from RequireAtLeastOnestaje się { title: string, icon: string}, czyli niezmienionymi właściwościami kluczy nieuwzględnionych w'click' | 'component'
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys]od RequireAtLeastOnestaje
{
component: Required<{ component?: number }> & { click?: number },
click: Required<{ click?: number }> & { component?: number }
}[Keys]
Który staje się
{
component: { component: number, click?: number },
click: { click: number, component?: number }
}['component' | 'click']
Który w końcu się staje
{component: number, click?: number} | {click: number, component?: number}
Punkt przecięcia kroków 1 i 2 powyżej
{ title: string, icon: string}
&
({component: number, click?: number} | {click: number, component?: number})
upraszcza do
{ title: string, icon: string, component: number, click?: number}
| { title: string, icon: string, click: number, component?: number}