Typy warunkowe w TypeScript


65

Zastanawiałem się, czy mogę mieć typy warunkowe w TypeScript?

Obecnie mam następujący interfejs:

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

Ale chcę usunąć errorTexti mieć to tylko wtedy, gdy isValidjest falseto wymagana właściwość.

Chciałbym móc go napisać jako następujący interfejs:

interface ValidationResult {
  isValid: true;
}

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

Ale jak wiadomo, nie jest to możliwe. Jaki masz pomysł na tę sytuację?


Czy to znaczy, kiedy isValidjest false?
CertainPerformance,

18
isValid jest wtedy zbędny. Równie dobrze możesz mieć errorText, a jeśli errorText ma wartość null, nie ma błędu.
MTilsted,

Tak, @MTilsted, masz rację, ale musimy to zachować z powodu naszych starszych kodów.
Arman,

Odpowiedzi:


89

Jednym ze sposobów modelowania tego rodzaju logiki jest użycie typu unii, coś takiego

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" }
}

Kompilator może następnie zawęzić typ w oparciu o flagę logiczną

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

7
Niezła odpowiedź. I fascynujące, że kompilator może powiedzieć, że tutaj rmusi być tego typu Invalid.
sleske,

1
Nazywa się to dyskryminowanymi związkami. Całkiem fajne rzeczy: typescriptlang.org/docs/handbook/...
Umur Kontacı

41

Aby uniknąć tworzenia wielu interfejsów, które przyzwyczają się tylko do utworzenia trzeciego, możesz również zmienić bezpośrednio type:

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

20

Unia wykazać błędy sposób obsługi to polecam. Niemniej jednak, maszynopis nie ma czegoś znanego jako „ typy warunkowych ” i można je obsługiwać to.

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


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

To ValidationResult(co jest tak naprawdę ValidationResult<boolean>spowodowane domyślnym parametrem) jest równoważne związkowi utworzonemu w odpowiedzi na błędy lub w odpowiedzi CertainPerformance i może być używane w ten sam sposób.

Zaletą tego jest to, że możesz również przekazywać znaną ValidationResult<false>wartość, a wtedy nie będziesz musiał testować isValidtak, jakby był znany falsei errorStringistniałby. Prawdopodobnie nie jest to konieczne w przypadku tego typu - a typy warunkowe mogą być złożone i trudne do debugowania, więc prawdopodobnie nie należy ich używać niepotrzebnie. Ale mogłeś i to wydawało się warte wspomnienia.


3
Jestem pewien, że czasami są bardzo pomocne. Ale dla mnie składnia wygląda naprawdę paskudnie.
Peilonrayz,

3
@Peilonrayz Eh, ma dobrą spójność z innymi kodowaniami maszynopisu i extendsjest właściwym operatorem do użycia. I ma bardzo silny, zwłaszcza, że można również używać go kopać w rodzaju: type SecondOf<T> = T extends Pair<any, infer U> ? U : never;.
KRyan,

@KRyan Myślałem o tym. Czy to po prostu oznacza SecondOf<number>„rozszerza się” Pair<any, number>? Wydaje mi się, że trafne jest powiedzenie „nie oceniaj książki po okładce”.
Peilonrayz,

@Peilonrayz Ah, nie; raczej przeciwnie. SecondOf<Pair<any, number>>ocenia na number. SecondOf<number>ocenia never, ponieważ number extends Pair<any, infer U>jest fałszywy, ponieważ numbernie rozszerza żadnegoPair
KRyan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.