过滤对象数组时可选 属性 的 TypeGuard

TypeGuards for optional property while filtering array of objects

我在打字稿中有以下接口:

interface ComplexRating {
  ratingAttribute1?: number;
  ratingAttribute2?: number;
  ratingAttribute3?: number;
  ratingAttribute4?: number;
}

export interface Review {
  rating: ComplexRating | number;
}

为了简单起见,我想计算 ratingAttribute1 的平均评分。

鉴于这些评论:

const reviews: Review[] = [
  { rating: { ratingAttribute1: 5 } },
  { rating: { ratingAttribute1: 10 } },
  { rating: { ratingAttribute2: 15 } },
  { rating: 5 }
]

我可以筛选出我感兴趣的评论,即:

const calculateAverageRating = (reviews: Review[]): number => {
  const reviewsWithRating = reviews.filter(
    (review) =>
      typeof review.rating === 'object' &&
      typeof review.rating['ratingAttribute1'] === 'number'
  );
  return (
    reviewsWithRating.reduce((acc, review) => {
      let newValue = acc;
      if (typeof review.rating === 'object') {
        const rating = review.rating['ratingAttribute1'];
        if (rating) {
          newValue += rating;
        }
      }
      return newValue;
    }, 0.0) / reviewsWithRating.length
  );
};

现在,令人恼火的是 Typescript 不知道通过 运行 我输入的 reviews.filter 函数只保护评论的子集 rating of type ComplexType 并且那些 ratingAttribute1: number; 而不是 ratingAttribute?: number.

我希望最终得到的结果是不必在计算中有效地重复类型检查,最终得到:

const calculateAverageRating = (reviews: Review[]): number => {
  const reviewsWithRating = reviews.filter(
    (review) =>
      typeof review.rating === 'object' &&
      typeof review.rating['ratingAttribute1'] === 'number'
  );
  return (
    reviewsWithRating.reduce(
      (acc, review) => acc + review.rating['ratingAttribute1'],
      0.0
    ) / reviewsWithRating.length
  );
};

但这不是开箱即用的:

有什么方法可以实现这种级别的类型保护吗?或者有没有更好的方法来做这类事情?

.filter() 函数总是 return 一个与参数相同类型的数组。这就是 reviewsWithRating 仍然是 Review[] 的原因,即使在您对其进行过滤之后也是如此。

要更改此设置,您可以向回调添加 type guard

const reviewsWithRating = reviews.filter(
  (review): review is { rating: Required<ComplexRating> } =>
    typeof review.rating === 'object' &&
    typeof review.rating['ratingAttribute1'] === 'number'
);

现在 TypeScript 会知道 reviewsWithRating 的类型是 { rating: Required<ComplexRating> }[]