继续学习TypeScript | 青训营笔记

55 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天

今天继续来学习TypeScript,以下仅为个人的学习记录随笔,有错误以及不足的地方请多多包涵

联合/交叉类型

当我们在定义一些复杂的数据类型的时候,有可能定义接口的代码都比实际内容多,这样就显得异常繁琐,例如下图中,写了一个bookList类型,需要有历史书和故事书,分别给他们定义,而这还仅仅是两种类型的数,实际情况中更多,如果每种书都要全部从头到尾声明一遍的话是十分麻烦的

这里就衍生出了联合/交叉类型,他可以抽离出公共的属性,减少了重复书写 image.png 这里使用&符号连接了一个联合类型,指剩下的部分可以是其中的一项,但是必须要有author属性

image.png

类型保护与类型守卫

当我使用联合类型的时候,我们只能使用其公共部分,或者说理解为,只能使用已经确定一定会有的属性。举个例子

interface A { a: 1,a1: 2,c:3}
interface B { b: 1,b1: 2,c:3}

当我们使用A|B这个联合类型的时候(假设C是这个联合类型),你不能调用C.a或者C.b

否则TypeScript会报错告诉你不存在。因为C可能是A类型或者B类型,当为A类型时是没有b属性的,而当为B属性时,又是没有a属性的,但是c属性他们是公共有的,所以你只能调用C.c,这样子TypeScript才不会报错。

使用类型守卫我们就可以解决这个问题

interface IA {
  a: 1
  a1: 2
}
interface IB {
  b: 1
  b1: 2
}
;
/*类型守卫:定义一个函数,它的返回值是一个类型谓词,生效范围为子作用域*/
function getIsIA(arg: IA | IB): arg is IA {
  return !!(arg as IA).a
}
function log2(arg: IA | IB) {
  if (getIsIA(arg)) {
    console.log(arg.a1)
  } else {
    console.log(arg.b1)
  }
}

我们通过getIsIA这个方法来判断是否是IA类型,而这个方法叫做类型守卫,用了一些特殊的语法,规定了返回值是一个类型谓词,重点来了。当函数返回值为true的时候,那么这个arg的类型一定是IA

而且在之后调用getIsIA方法的if分支中,TypeScript都清楚的知道这个类型就是IA的而不是IB,反之在之后的else分支中,一定是IB类型而不是IA类型。

但如果每次判断类型都要写一个函数的话那么也太痛苦了,还有一种类型保护

typeof类型保护

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

这里引用一下TypeScript的官方文档的话,我觉得表述的非常清楚

这些* typeof类型保护*只有两种形式能被识别: typeof v === "typename"和 typeof v !== "typename", "typename"必须是 "number", "string", "boolean"或 "symbol"。 但是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护