关于TypeScript联合类型报表达式有误的问题

1,333 阅读3分钟

关于TypeScript联合类型报表达式有误的问题

今天在工作时使用TypeScript联合类型导致报了一个“表达式有误”的错,因为这个错误出现的位置是在reduce方法上,一个数组使用reduce居然报错了。

问题描述

课程和考试接口都有一个状态码,他们分别标识这个课程或考试是否完成,现在我需要计算出他们课程和考试的完成进度。由于他们代码逻辑是一样的,不一样的是传入的数组和状态的字段,所以我选择写一个函数对他们进行复用。

代码如下

interface Course{
  courseStatus:boolean
}

interface Exam{
  examStatus:boolean
}

const getCount = (isCourse: boolean) => {
  const feild = isCourse?'courseStatus':'examStatus'
  return (list: Course[] | Exam[]) => {
    return list.reduce((total,curr) => {
      return total+(curr[feild]?1:0)
    },0)
  }
}

const getCourseCount = getCount(true)
const getExamCount = getCount(false)

但是这个时候却出现了一个报错。

image-20210513003720962.png

这是为啥了,报错显示我的list.reduce的表达式有误。

解决过程

我检查了一下我的list是一个数组,数组肯定是有reduce的呀,为什么会表达式有误,我把上面联合类型去掉一个类型后就没有报错了。知其然还得知其所以然,于是就疯狂翻文档,无果,去摸鱼群问问大佬们。

大佬一眼就看出是联合类型的问题,还纠正了我的写法,应改为

//这块代码是正确写法
interface Course{
  courseStatus:boolean
}

interface Exam{
  examStatus:boolean
}

const getCount = (isCourse: boolean) => {
  const feild = isCourse?'courseStatus':'examStatus'
  return (list: (Course | Exam)[]) => {
  //return (list: Course[] | Exam[]) => {
    return list.reduce((total,curr) => {
      return total+(curr[feild]?1:0)
    },0)
  }
}

这是为什么呢?

如果是以"course[] | Exam[]"为参数类型的话,方法会以course[]和Exam[]作为参数类型分别跑一遍函数,但是当course[]作为参数类型的时候没有examStatus这个参数,所以报错了,反之亦然。没理解的直接看下面代码。

interface Course{
  courseStatus:boolean
}

interface Exam{
  examStatus:boolean
}
//ts语法检查
//以Course[]作为类型检查一遍
const getCount = (isCourse: boolean) => {
  const feild = isCourse?'courseStatus':'examStatus'
  return (list: Course[]) => {
    return list.reduce((total,curr) => {
      //检查到这里发现feild的取值是courseStatus和examStatus其中一个。这里如果feild取了examStatus,上面传入的list类型里没有examStatus,所以报错了。
      return total+(curr[feild]?1:0)
    },0)
  }
}
//再以Exam[]作为类型检查一遍
const getCount = (isCourse: boolean) => {
  const feild = isCourse?'courseStatus':'examStatus'
  return (list: Exam[]) => {
    return list.reduce((total,curr) => {
      //这个跟上面同理,feild取了courseStatus,上面传入的list类型里没有courseStatus,所以报错了。
      return total+(curr[feild]?1:0)
    },0)
  }
}

所以,当接口拥有相同属性的时候也可以不会有以上的报错,稍微改了一下接口的属性名和去掉方法无用的部分,得出:

interface Course{
  status:boolean
}

interface Exam{
  status:boolean
}

const getCount = (list: Course[] | Exam[]) => {
  return list.reduce((total,curr) => {
      return total+(curr.status?1:0)
    },0)
}

这种情况下是不会报错的,因为Course[]和Exam[]中都有status这个字段名。

如果是以"(course | Exam)[]"为参数类型的话,代码检测可以看成是把2个接口合成一个新的接口。

interface Course{
  courseStatus:boolean
}

interface Exam{
  examStatus:boolean
}
/***
 * ts语法检查
 * 以(Course|Exam)[]作为类型检查一遍,(Course|Exam)相当于把2个接口相交在一起,形成一个新的接口,即:
 * interface newType{
 * 	courseStatus:boolean
 *  examStatus:boolean
 * }
 * 所以在新的接口下,同时拥有courseStatus和examStatus属性,就不会报错。
 */

const getCount = (isCourse: boolean) => {
  const feild = isCourse?'courseStatus':'examStatus'
  return (list: (Course|Exam)[]) => {
    return list.reduce((total,curr) => {
      return total+(curr[feild]?1:0)
    },0)
  }
}

结语

由于本人经验水平有限,难免会有纰漏,对此欢迎指正。