TypeScript实战
上QQ阅读APP看书,第一时间看更新

3.1 条件判断

我们知道,现实生活中很多事物在不同的条件下会有不同的结果,比如人生,往往选择很重要,在人生的分叉路口,一个人如何选择往往决定其后续的发展轨迹。一个程序的运行也是如此,在不同的条件下需要执行不同的操作,这也是程序的魅力所在,只要设计合理,就可以根据实际情况执行合理的逻辑。在某种程度上来说,程序的“智能”特征依赖于条件判断。

条件语句是一种根据条件执行不同代码的语句,如果条件满足就执行一段代码,否则执行其他代码。条件语句表达的意图可以理解为对某些事的决策规则或者表达某种关系,即“如果什么,则什么”。

例如,公司的员工需要请假,那么每个公司都会制定一套请假的流程来确定请假的规则。例如,首先员工发起请假申请,然后员工的主管进行审批,审批后会根据条件判断来确定下一步的审批人是谁,如果请假天数大于3天,由于请假天数比较多,请假单会在主管审批后流转到总经理处进行审批,总经理处审批后再流转到HR处审批,以便做好考勤工作;如果请假天数小于等于3,那么请假单会在主管审批后流转到HR处进行审批,HR审批通过后,流程结束。具体的流程示意如图3.1所示。

图3.1 请假流程图

如果用TypeScript语言来构建一个请假流程的程序,就必须用到条件判断的相关知识。正是有了条件判断,从而使得程序可以根据规则来灵活处理现实问题。在TypeScript语言中,实现条件判断的方法有多种,例如if、if…else和switch。下面分别对这几种做详细说明。

3.1.1 if、if…else

程序实现条件判断最简单的就是if,在TypeScript语言中,if语法和JavaScript基本一致。为了直观了解if语法,假设有一个函数getGrade,其中有一个数值类型的参数money,如果参数money大于8000,那么等级为A;如果参数money大于3500且小于等于8000,那么等级为B;如果参数money大于900且小于等于3500,那么等级为C;其他情况等级为D。具体内容如代码3-1所示。

【代码3-1】函数getGrade用if实现代码: ts001.ts

    01  /**
    02   * 根据money参数给出等级A,B,C,D
    03   * @param money
    04   * @returns 字符串 A,B,C,D
    05   */
    06  function getGrade(money: number) {
    07      if (money > 8000) {
    08          return "A";
    09      }
    10      if (money <= 8000 && money>3500) {
    11          return "B";
    12      }
    13      if (money <= 3500 && money>900) {
    14          return "C";
    15      }
    16      return "D";
    17  }
    18  let a = getGrade(8002);
    19  console.log(a);       //A
    20  a = getGrade(5000);
    21  console.log(a);       //B
    22  a = getGrade(3200);
    23  console.log(a);       //C

提示

一般来说,if语句所判定的条件必须是全集,否则可能会出现异常。

另一个常用的条件判断为if ... else。还以上面的逻辑为例,这次将if换成if ...else进行编码,具体内容如代码3-2所示。

【代码3-2】函数getGrade用if ... else实现代码: ts002.ts

    01  /**
    02   * 根据money参数给出等级A,B,C,D
    03   * @param money
    04   * @returns 字符串 A,B,C,D
    05   */
    06  function getGrade(money: number) {
    07      if (money > 8000) {
    08          return "A";
    09      }
    10      else if (money <= 8000 && money>3500) {
    11          return "B";
    12      }
    13      else if (money <= 3500 &&  money>900) {
    14          return "C";
    15      }
    16      else
    17          return "D";    //只有一条语句,那么可以省略{}
    18  }
    19  let a = getGrade(8002);
    20  console.log(a);             //A
    21  a = getGrade(5000);
    22  console.log(a);             //B
    23  a = getGrade(3200);
    24  console.log(a);             //C

提示

if或者else下如果只有一条语句,那么可以省略{},但是建议保留,提高代码的可读性。

另外一个需要注意的是,在条件判定语句中,不要出现无法到达的语句,如下面的代码3-3中08行代码永远无法执行。默认情况下此代码不会报错,需要指定TypeScript中的--allowUnreachableCode编译选项为false,用于检测此类错误。

【代码3-3】函数ferror实现代码: ts003.ts

    01  function ferror(a) {
    02      if (a) {
    03         return true;
    04      }
    05      else {
    06         return false;
    07      }
    08      return 1;         //无法到达的代码
    09  }

第1章已经简单介绍了如何安装开发工具Visual Studio Code,并学会用命令tsc对TypeScript文件进行编译,但并未介绍tsc命令的编译选项如何开启。下面就在Visual Studio Code工具中对如何配置tsc的编译选项进行说明。

在Visual Studio Code中,一般来说,一个TypeScript项目就对应一个文件夹。首先用Visual Studio Code打开charpter3文件夹,新建一个tsconfig.json文件。如果一个目录里存在一个tsconfig.json文件,那么编译器就认为这个目录是TypeScript项目的根目录。 tsconfig.json文件中指定了用来编译这个项目的若干配置和编译选项。tsconfig.json的具体配置如图3.2所示。

图3.2 tsconfig.json配置

不带任何参数的情况下调用tsc命令,编译器会从当前目录开始去查找tsconfig.json文件,如果找不到就逐级向上搜索父目录。当找到tsconfig.json时就会解析它,并按照相应配置去编译。

在tsconfig.json文件中,在"compilerOptions"节点下配置"allowUnreachableCode":false来开启--allowUnreachableCode编译选项。

然后在Visual Studio Code中新建一个文件并命名为ts003.ts,其内容如代码3-3所示。这时Visual Studio Code编辑器会提示检测到不可达的代码(unreachable code detected.),具体提示如图3.3所示。

图3.3 Visual Studio Code检测到不可达代码提示界面

另外一种方法是,也可以用tsc命令来编译代码,查看代码是否有不可达的代码段。tsc命令为:

    tsc --allowUnreachableCode false .\ts003.ts

将此命令在Visual Studio Code中的terminal终端中执行时,编译器也会告知同样的错误,具体如图3.4所示。

图3.4 Visual Studio Code用tsc命令编译界面

从上面的两种方法对比来看,第一种方法通过配置tsconfig.json文件会更加方便,可以让编辑器自动检测错误。第二种方法只能在调用tsc编译的时候才能发现问题。

这个特性能捕获到的一个更不易发觉的错误是在return语句后添加换行语句而导致的检测到不可及的代码。因为TypeScript代码最后会编译成JavaScript执行,而JavaScript会自动在行末结束return语句。换句话说,如果在return行末没有其他语句,那么会自动添加分号(;),进而结束函数体。return后面的代码相当于永不执行,变成了一个无法可达的代码区域,如代码3-4所示。

【代码3-4】函数ferror_return实现代码: ts004.ts

    01  function ferror_return () {
    02      return  // 换行导致自动插入分号;
    03      {
    04          x: "string"   // 检测到不可及的代码
    05      }
    06  }

3.1.2 嵌套if

当某些条件判断需要判定的参数有多个,或者逻辑比较复杂时,可能单层if不能很好地解决问题,这时需要对if语句进行嵌套来解决。代码3-5给出了嵌套if的示例。

【代码3-5】函数getGrade嵌套if实现代码: ts005.ts

    01  /**
    02   * Sex枚举
    03   */
    04  enum Sex {
    05      Man = 1,
    06      Female = 2,
    07  }
    08  /**
    09   * 根据money和sex参数给出等级A,B,C,D
    10   * @param money
    11   * @param sex
    12   * @returns 字符串 A,B,C,D
    13   */
    14  function getGrade(money:number,sex:Sex){
    15      if(sex === Sex.Man){
    16          if (money > 8000) {
    17              return "A";
    18          }
    19          else if (money <= 8000 && money>3500) {
    20              return "B";
    21          }
    22          else if (money <= 3500 &&  money>900) {
    23              return "C";
    24          }
    25          else
    26              return "D";
    27      }
    28      else{
    29          if (money > 7000) {
    30              return "A";
    31          }
    32          else if (money <= 7000 && money>3000) {
    33              return "B";
    34          }
    35          else if (money <= 3000 &&  money>800) {
    36              return "C";
    37          }
    38          else
    39              return "D";
    40      }
    41  }
    42  let a = getGrade(7100,Sex.Man);
    43  console.log(a);       //B
    44  a = getGrade(7100,Sex.Female);
    45  console.log(a);       //A

提示

if嵌套不宜过多,一般来说超过2层代码的可读性就会大大降低。

3.1.3 switch

除了if和if ... else以外,switch也可以实现条件判断,但要注意switch语句中的每一个case表达式类型必须与switch表达式类型相匹配,否则会报错。

另外,switch中的case语句块如果有逻辑语句,不是空的,那么必须用break结尾,否则程序会贯穿case区域,导致结果错误。代码3-6给出了switch的基本用法。

【代码3-6】函数swithDemo1实现代码: ts006.ts

    01  /**
    02   * 判断奇偶数
    03   * @param num
    04   * @returns 字符串
    05   */
    06  function swithDemo1(num:number){
    07      let ret  = "";
    08      switch(num % 2){
    09          case 0 : {
    10              ret = "偶数";
    11              break;       //在非空情况下break不能少,否则程序会贯穿此case区域
    12          }
    13          case 1: {
    14              ret = "奇数";
    15              break;
    16          }
    17      }
    18      return ret;
    19  }
    20  let a = swithDemo1(201);
    21  console.log(a);          //奇数
    22  a = swithDemo1(202);
    23  console.log(a);          //偶数

在代码3-6中,如果11行处将break去掉或者注释掉,那么swithDemo1(202)返回"奇数",而不是"偶数"。缺少break程序不会跳出switch的{}块,而是继续执行case 1:{ },也就是将ret重新赋值为"奇数"。

提示

TypeScript现支持对当switch语句case中出现贯穿时报错的检测。这个检测默认是关闭的,可以使用--noFallthroughCasesInSwitch启用。

参考前面的tsconfig.json编译项配置,用"noFallthroughCasesInSwitch":true开启配置。那么在Visual Studio Code中打开ts006.ts文件时,如果将11行的break注释掉,则编译器会提示穿透switch语句中的case错误(Fallthrough case in swith.)。检测提示界面如图3.5所示。

图3.5 Visual Studio Code穿透switch提示界面

另外,JavaScript语言中没有返回值的代码分支会隐式地返回undefined。这个特征往往会导致程序不能按照预期执行。现在TypeScript编译器可以将这种方式标记为隐式返回,虽然TypeScript编译器对于隐式返回的检查默认是被关闭的,但是可以使用--noImplicitReturns来启用。代码3-7给出了检测隐式返回undefined的示例。

【代码3-7】函数freturn隐式返回代码: ts007.ts

    01  function freturn(x) { // 错误: 不是所有分支都返回了值
    02      if (x) {
    03          return false;
    04      }
    05      // 隐式返回了undefined
    06  }

在tsconfig.json文件中用"noImplicitReturns":true开启编译选项,那么在Visual Studio Code中打开ts007.ts文件时编译器会提示不是所有路径返回值的错误(Not all code paths return a value.)。检测提示界面如图3.6所示。

图3.6 Visual Studio Code提示不是所有路径返回值界面