写在前面
距 typescript 3.7
正式发布已经有一段时间了,这段时间正在对手上的项目进行 typescript
的迁移工作,所以会特别留意每一次的 release
。
对于 3.7
中包含的新特性,其实相比较之前几次 release
来说,算是一个比较小的发布版本,但是其中包含的几个特性对代码质量本身,会带来显著地提升。
Optional Chaining
首先第一个特性是对于 optional chaining
操作符的支持,翻译过来应该可以叫做 可选链操作符,当然我还是觉得这样翻译有点怪怪的,暂且就直接用英文好了。
这个特性首先是 es2019
中包含的一个新特性,对于特性本身,有兴趣的可以参考这里。
由于 typescript
是 javascript
的超集,所以预先实现这个特性也是在预料之内的事情,大概使用方式是这样的:
a?.b();
等价于:
if(a) a.b();
// 或者
a && a.b()
如果是多层嵌套,比如 b
也是一个对象,要继续调用 c()
,那么可以这样:
a?.b?.c()
但其实就算这样写的话,它也不是安全的,因为 b()
中的 b
也有可能是空值,直接调用的话,也会抛出异常。为了绝对的安全,可以这样写:
a?.b?.();
值得注意的是,这里一定要对于 可选 的含义有一个正确的理解,可选 的意思是,它在类型的声明中,通过 ?
来修饰,代表一个类型包含某个可为空值的属性。言外之意的意思就是,?.
不会对那些不符合类型声明本身的属性调用,比如:
interface A {}
const a: A = {};
a?.b?.(); // Property 'b' does not exist on type 'A'
除非 A
接口的声明改为:
interface A {b?: any}
这个特性在项目的实践意义是很大的,我们可以写更少的 if
断言语句或者 &&
操作符,但是却达到了相同的效果。
Nullish Coalescing
中文翻译过来会叫做双问号操作符,这个其实挺形象的,因为它的语法确实就是 ??
。
这个操作符的功能,往简单说,就是为一个空值,指定一个默认值,类似下面的代码:
let a = b || 'foo'
当 b
为空值时,由于 ||
操作符的特性,a
的值会被赋予 foo
。如果使用 ??
操作符进行改写,如下:
let a = b ?? 'foo'
表面上看,似乎两者没什么区别,但其实这里隐含了一个问题,就是 ||
对于空值的概念,并不仅仅指 null
和 undefined
,类似 false
、0
等一系列逻辑上为 false
的值都会算作空值,这显然是有问题的,比如:
const b = 0
let a = b || 'foo'
// a 为 'foo'
这个示例中,我们期望 a
只有在 b
为真正意义上的空值(null
或者 undefined
)时,才被赋予默认值,a
应当等于 0
,而实际运行结果确实 foo
,因为 b=0
,在 ||
操作符的运行过程中,它会被解释为 false
。我曾在实际项目中,编写过一个验证码组件,很不幸,踩上了这个坑,当时为了 debug
这个问题,花了很长时间。
但使用 ??
操作符,就不会存在这个问题。
Uncalled Function Checks
我相信很多人都曾经遇过类似的问题,因为缺乏有效的命名规范,断言属性和断言方法会在实际项目中被混用,比如:
class A {isFoo(): boolean {return false;}
}
function test(a: A) {if (a.isFoo) {...}
}
这里如果我们的本意是要通过调用 a.isFoo
来获取一个断言值,我们明显犯了一个错误,我们应当使用 if (a.isFoo())
,而不是直接 if (a.isFoo)
,因为后者虽然在语法层面没有错误,但是在逻辑含义,它将被断言为 true
。但在 3.7
发布之后,typescript
会尝试帮助我们发现这个问题。
虽然如此,但我仍然建议大家针对断言方法和断言属性制定统一的命名规范,比如 isXXX
代表属性,而 assertXXX
代表方法。
其他
其他的一些变更,均是易用性上的一些改变,比如:
-
Flatter Error Reporting
:会将一大段的类型重复的错误日志,尽可能地压缩为单条、更准确、更精简的错误日志 - 文件级别的
@ts-nocheck
:之前版本中该注解仅支持行内级别 - 递归类型声明:能够在类型声明中,使用递归语法来声明更复杂的类型,比如
json
类型 - 对
js
文件提供declaration
支持,以减小从js
项目迁移的迁移成本
关注公众号 全栈 101
,只谈技术,不谈人生