Bedingte Typen in TypeScript


65

Ich habe mich gefragt, ob ich bedingte Typen in TypeScript haben kann.

Derzeit habe ich die folgende Schnittstelle:

interface ValidationResult {
  isValid: boolean;
  errorText?: string;
}

Aber ich möchte entfernen errorTextund habe es nur, wenn isValides falseals erforderliche Eigenschaft ist.

Ich wünschte, ich könnte es als folgende Schnittstelle schreiben:

interface ValidationResult {
  isValid: true;
}

interface ValidationResult {
  isValid: false;
  errorText: string;
}

Aber wie Sie wissen, ist das nicht möglich. Was ist Ihre Vorstellung von dieser Situation?


Meinst du, wann isValidist false?
— Bestimmte Leistung

18
isValid ist dann redundant. Sie können auch nur den errorText verwenden. Wenn errorText dann null ist, liegt kein Fehler vor.
— MTilsted

Ja, @MTilsted, Sie haben Recht, aber wir müssen es aufgrund unserer alten Codes behalten.
— Arman

Antworten:


89

Eine Möglichkeit, diese Art von Logik zu modellieren, besteht darin, einen Unionstyp zu verwenden

interface Valid {
  isValid: true
}

interface Invalid {
  isValid: false
  errorText: string
}

type ValidationResult = Valid | Invalid

const validate = (n: number): ValidationResult => {
  return n === 4 ? { isValid: true } : { isValid: false, errorText: "num is not 4" }
}

Der Compiler kann dann den Typ basierend auf dem Booleschen Flag eingrenzen

const getErrorTextIfPresent = (r: ValidationResult): string | null => {
  return r.isValid ? null : r.errorText
}

7
Gute Antwort. Und faszinierend, dass der Compiler tatsächlich erkennen kann, dass rdies hier vom Typ sein muss Invalid.
— Sleske

1
Dies nennt man diskriminierte Gewerkschaften. Ziemlich cool stuff: typescriptlang.org/docs/handbook/...
— Umur Kontacı

41

Um zu vermeiden, dass mehrere Schnittstellen erstellt werden, die nur zum Erstellen einer dritten verwendet werden, können Sie typestattdessen auch direkt abwechseln :

type ValidationResult = {
    isValid: false;
    errorText: string;
} | {
    isValid: true;
};

20

Die durch Fehler demonstrierte Vereinigung ist, wie ich empfehle, damit umzugehen. Trotzdem hat Typescript so etwas wie " bedingte Typen ", und sie können damit umgehen.

type ValidationResult<IsValid extends boolean = boolean> = (IsValid extends true
    ? { isValid: IsValid; }
    : { isValid: IsValid; errorText: string; }
);


declare const validation: ValidationResult;
if (!validation.isValid) {
    validation.errorText;
}

Dies ValidationResult(was tatsächlich ValidationResult<boolean>auf den Standardparameter zurückzuführen ist) entspricht der Vereinigung, die in der Antwort von Bugs oder in der Antwort von CertainPerformance erzeugt wird , und kann auf dieselbe Weise verwendet werden.

Der Vorteil hierbei ist, dass Sie auch einen bekannten ValidationResult<false>Wert weitergeben können und dann nicht testen müssen, isValidwie es bekannt ist falseund errorStringbekanntermaßen existiert. In einem solchen Fall wahrscheinlich nicht erforderlich - und bedingte Typen können komplex und schwer zu debuggen sein, sodass sie wahrscheinlich nicht unnötig verwendet werden sollten. Aber Sie konnten, und das schien erwähnenswert.


3
Ich bin sicher, dass diese manchmal wirklich hilfreich sind. Aber für mich sieht die Syntax wirklich böse aus.
— Peilonrayz

3
@Peilonrayz Eh, es hat eine gute Konsistenz mit anderen Typescript-Codierungen und extendsist der richtige Operator. Und es ist extrem leistungsfähig, zumal Sie es auch verwenden können, um in Typen zu graben : type SecondOf<T> = T extends Pair<any, infer U> ? U : never;.
— KRyan

@KRyan Ich habe darüber nachgedacht. Bedeutet das im Grunde nur SecondOf<number>"erweitert" Pair<any, number>? Ich denke, das Sprichwort "Beurteile ein Buch nicht nach seinem Einband" ist hier relevant.
— Peilonrayz

@Peilonrayz Ah, nein; eher das Gegenteil. SecondOf<Pair<any, number>>bewertet zu number. SecondOf<number>bewertet never, weil number extends Pair<any, infer U>falsch ist, wie numberkeine verlängertPair
— KRyan
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.