这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
今天继续来学习TypeScript,以下仅为个人的学习记录随笔,有错误以及不足的地方请多多包涵
联合/交叉类型
当我们在定义一些复杂的数据类型的时候,有可能定义接口的代码都比实际内容多,这样就显得异常繁琐,例如下图中,写了一个bookList类型,需要有历史书和故事书,分别给他们定义,而这还仅仅是两种类型的数,实际情况中更多,如果每种书都要全部从头到尾声明一遍的话是十分麻烦的
这里就衍生出了联合/交叉类型,他可以抽离出公共的属性,减少了重复书写
这里使用&符号连接了一个联合类型,指剩下的部分可以是其中的一项,但是必须要有author属性
类型保护与类型守卫
当我使用联合类型的时候,我们只能使用其公共部分,或者说理解为,只能使用已经确定一定会有的属性。举个例子
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并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护