我怎样才能对同一个对象进行类型保护?

How can I do type-guarding for the same object?

比如我有这样一段代码:

interface Subscriber {
  reviewData: string | string[]
  hasArray: boolean;
}

const reviews = ["Good", "Not bad"];

const subscriber: Subscriber = {
  reviewData: reviews,
  hasArray: Array.isArray(reviews)
}

if (subscriber.hasArray) {
  subscriber.reviewData.forEach(console.log);
} else {
  console.log(subscriber.reviewData);
}

当然,TypeScript 会在 subscriber.reviewData.forEach(console.log); 处抱怨说它无法定义它是一个数组还是一个字符串,因为条件 subscriber.hasArray.[=16 中没有足够的类型保护=]

我不想总是写 Array.isArray(subscriber.reviewData) 因为我会在很多其他地方使用这个逻辑。

我听说过 is,但在我的场景中情况并非如此,因为使用 is 意味着您必须有一个函数并将一些参数传递给它。

怎样才能达到预期的效果?

如果您希望能够使用 hasArray 来确定 reviewDatastring[] 还是 string,那么您确实需要 Subscriber 成为 discriminated union type 而不是接口。您的界面将允许 {reviewData: "", hasArray: true},因此编译器无法排除这种可能性。使用可区分的联合,您可以检查一个 可区分的 属性 以缩小整个对象的类型:

type Subscriber = {
    reviewData: string[]
    hasArray: true;
} | {
    reviewData: string
    hasArray: false;
}

在这种情况下 hasArray 是布尔值 literal type truefalse.

的判别式 属性

然后,给定一个 Subscriber:

const subscriber: Subscriber = Math.random() < 0.5 ?
    { reviewData: "Good", hasArray: false } :
    { reviewData: ["Who", "Cares"], hasArray: true };

我们可以按照您想要的方式进行检查,不会出现编译错误:

if (subscriber.hasArray) {
    subscriber.reviewData.forEach(console.log);
} else {
    console.log(subscriber.reviewData.toUpperCase());
}

Playground link to code