Typescript-interface med ANTINGEN båda egenskaperna eller INGA
Jag har nyckel-värde-par som 'kommer i par'. Med andra ord, om jag har ett nyckel-värde-par A:B vill jag också kräva att mitt objekt har C:D, men det kan också helt sakna båda paren. (Tänk message:string för ett par och min-length:number för det andra paret.) Finns det ett elegant sätt att skapa ett gränssnitt som består av ett godtyckligt antal sådana par?
Redigera: För tydlighetens skull vill jag designa ett gränssnitt för objekt där dessa skulle vara tillåtna:
{
//First pair
message1: string;
minLength1: number;
//Second pair
message2: string;
minLength2: number;
// ...
}
{
//First pair omitted altogether
//Just the second pair
message2: string;
minLength2: number;
}
... men där objekt som följande INTE är tillåtna eftersom du bara har ena halvan av ett par:
{
//First pair
message1: string;
// minLength1: number; // Error †
// ...
}
† Fel: om message1 inkluderas måste du också inkludera minLength1
Svar #1
För ett givet par av egenskaper är den närmaste approximationen du kan göra för att kräva både egenskaperna eller ingen av dem en union av typen med båda egenskaperna och en typ med valfria egenskaper av typen never (vilket förhindrar att egenskaperna är närvarande med ett värde annat än undefined). Jag fick idén från detta svar. Detta kommer att ge dig ett fel i rätt fall, även om felmeddelandet kanske inte är särskilt hjälpsamt. Sedan kan du helt enkelt skärma unionerna för alla par av egenskaper du vill (så länge du inte har mer än omkring 10 par, eftersom när TypeScript förenklar en typ, distribuerar den snitt av unioner för att skapa unioner av snitt, vilket kommer att få typen att växa exponentiellt). För ditt exempel:
type AllOrNone<T> = T | {[K in keyof T]?: never};
type MyType = AllOrNone<{
message1: string;
minLength1: number;
}> & AllOrNone<{
message2: string;
minLength2: number;
}>;
Om du verkligen blir störd av undefined-fallet och är villig att använda en generisk funktion för att validera dina objekt istället för att skriva ner en enskild typ, då kan du göra något i stil med detta:
type CheckAllOrNone<A, T> =
T | (keyof A & keyof T extends never ? {} : never);
type CheckMyType<A> = CheckAllOrNone<A, {
message1: string;
minLength1: number;
}> & CheckAllOrNone<A, {
message2: string;
minLength2: number;
}>;
function checkMyType<A extends CheckMyType<A>>(arg: A) {
return arg;
}
