Умовні типи в TypeScript


65

Мені було цікаво, чи можу я мати умовні типи в TypeScript?

В даний час у мене є такий інтерфейс:

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

Але я хочу видалити errorTextта мати його лише тоді, коли isValidце falseє необхідною властивістю.

Я б хотів, щоб я зміг записати це як наступний інтерфейс:

interface ValidationResult {
  isValid: true;
}

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

Але, як відомо, це неможливо. Отже, яке ваше уявлення про цю ситуацію?


Ви маєте на увазі, коли isValidце false?
Окреме виконання

18
isValid тоді зайвий. Ви можете просто мати errorText, і тоді, якщо errorText є нульовою, помилки немає.
MTilsted

Так, @MTilsted, ви праві, але ми повинні зберігати це через наші застарілі коди.
Арман

Відповіді:


89

Один із способів моделювання такої логіки - використовувати тип об'єднання, щось подібне

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

Потім компілятор може звузити тип на основі булевого прапора

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

7
Гарна відповідь. І захоплююче, що компілятор може насправді сказати, що тут rмає бути тип Invalid.
sleske

1
Це називається дискримінаційними спілками. Досить цікавий матеріал: typescriptlang.org/docs/handbook / ...
Umur Kontacı

41

Щоб уникнути створення декількох інтерфейсів, які використовуються лише для створення третього, ви можете також чергувати безпосередньо, typeзамість цього:

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

20

Союз продемонстрував помилками , як я рекомендую її обробки. Проте, Машинопис дійсно є щось , відоме як « умовні типи » , і вони можуть впоратися з цим.

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


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

Це ValidationResult(що насправді ValidationResult<boolean>обумовлено параметром за замовчуванням) еквівалентно об'єднанню, яке виробляється у відповіді про помилки або у відповіді CertainPerformance , і може використовуватися таким же чином.

Перевага тут полягає в тому, що ви також можете обходити відоме ValidationResult<false>значення, і тоді вам не доведеться тестувати, isValidяк це було б відомо, falseі errorStringбуло б відомо, що існує. Напевно, це не потрібно для подібного випадку - а умовні типи можуть бути складними та важкими для налагодження, тому їх, ймовірно, не слід використовувати без потреби. Але ви могли, і це, здавалося, варто згадати.


3
Я впевнений, що вони справді корисні часом. Але, на мій погляд, синтаксис виглядає насправді бридко.
Пейлонрайз

3
@Peilonrayz Eh, він добре узгоджується з іншими кодуваннями Typescript і extendsє правильним оператором для використання. І це має дуже потужний, в зокрема , так як ви можете використовувати його , щоб вирити на типи: type SecondOf<T> = T extends Pair<any, infer U> ? U : never;.
KRyan

@KRyan Я думав про це. Це в основному просто означає SecondOf<number>"розширюється" на Pair<any, number>? Я гадаю, приказка "не судіть книгу за її обкладинкою" тут доречна.
Пейлонрайз

@Peilonrayz Ага, ні; скоріше навпаки. SecondOf<Pair<any, number>>оцінює до number. SecondOf<number>оцінює never, тому що number extends Pair<any, infer U>помилково, як numberне поширюється жоденPair
KRyan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.