Các loại điều kiện trong TypeScript


65

Tôi đã tự hỏi nếu tôi có thể có các loại điều kiện trong TypeScript?

Hiện tại tôi có giao diện sau:

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

Nhưng tôi muốn loại bỏ errorText, và chỉ có nó khi isValidfalsemột tài sản bắt buộc .

Tôi ước tôi có thể viết nó như giao diện sau:

interface ValidationResult {
  isValid: true;
}

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

Nhưng như bạn biết, điều đó là không thể. Vì vậy, ý tưởng của bạn về tình huống này là gì?


Bạn có nghĩa là, khi isValidfalse?
SurePerformance

18
isValid là dư thừa rồi. Bạn cũng có thể có errorText, và sau đó nếu errorText là null, không có lỗi.
MTilsted

Vâng, @MTilsted, bạn đúng, nhưng chúng tôi phải giữ nó vì mã di sản của chúng tôi.
Arman

Câu trả lời:


89

Một cách để mô hình hóa loại logic này là sử dụng loại kết hợp, đại loại như thế này

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

Trình biên dịch sau đó có thể thu hẹp kiểu xuống dựa trên cờ boolean

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

7
Câu trả lời tốt đẹp. Và thú vị là trình biên dịch thực sự có thể nói rằng rphải thuộc loại Invalidở đây.
sleske

1
Điều này được gọi là công đoàn phân biệt đối xử. Khá thứ mát: typescriptlang.org/docs/handbook/...
Umur Kontacı

41

Để tránh tạo nhiều giao diện chỉ được sử dụng để tạo một phần ba, bạn cũng có thể thay thế trực tiếp, typethay vào đó:

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

20

Các đoàn thể hiện bởi lỗi là cách tôi khuyên bạn nên xử lý này. Tuy nhiên, nguyên cảo không có cái gì gọi là “ loại có điều kiện ”, và họ có thể xử lý này.

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


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

Điều này ValidationResult(thực ra là ValidationResult<boolean>do tham số mặc định) tương đương với liên kết được tạo ra trong câu trả lời của lỗi hoặc trong câu trả lời của SurePerformance và có thể được sử dụng theo cách tương tự.

Ưu điểm ở đây là bạn cũng có thể vượt qua khoảng một tiếng ValidationResult<false>giá trị, và sau đó bạn sẽ không cần phải kiểm tra isValidvì nó sẽ được biết đến là falseerrorStringsẽ được biết là tồn tại. Có lẽ không cần thiết cho một trường hợp như thế này và các loại điều kiện có thể phức tạp và khó gỡ lỗi, vì vậy có lẽ chúng không nên được sử dụng một cách không cần thiết. Nhưng bạn có thể, và điều đó dường như đáng nói.


3
Tôi chắc chắn rằng những điều này thực sự hữu ích đôi khi. Nhưng, với tôi, cú pháp trông thực sự khó chịu.
Peilonrayz

3
@Peilonrayz Eh, nó có tính nhất quán tốt với mã hóa Bản mô tả khác và extendslà toán tử phù hợp để sử dụng. Và nó cực kỳ mạnh mẽ, đặc biệt vì bạn cũng có thể sử dụng nó để đào sâu vào các loại : type SecondOf<T> = T extends Pair<any, infer U> ? U : never;.
KRyan

@KRyan Tôi đã suy nghĩ về điều đó. Điều đó về cơ bản chỉ có nghĩa là SecondOf<number>'mở rộng' đến Pair<any, number>? Tôi đoán câu nói "đừng đánh giá một cuốn sách bằng bìa của nó" thích hợp ở đây.
Peilonrayz

@Peilonrayz À, không; đúng hơn là ngược lại. SecondOf<Pair<any, number>>đánh giá để number. SecondOf<number>đánh giá never, vì number extends Pair<any, infer U>là sai, vì numberkhông mở rộng bất kỳPair
KRyan
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.