// TypeScript 是JavaScript的“超集” //
// 前端语言中冉冉升起的新星 //
TypeScript是一种由微软开发的、开源的编程语言,近两年发展迅猛,越来越多的JavaScript项目正在迁移到TypeScript,主流前端框架及Node.js对TypeScript的支持也越来越友好。自2012年10月发布首个公开版本以来,它已得到了人们的广泛认可。
TypeScript发展至今,已经成为很多大型项目的标配,其提供的静态类型系统,大大增强了代码的可读性及可维护性;同时,它提供最新的和不断发展的JavaScript特性,能让我们构建更健壮的组件。
然而在TypeScript中,有些地方对“开箱即用”进行了限制,例如当使用一个未被声明过的变量时(当然,你可以为外部系统使用声明文件)。也就是说,传统的编程语言在类型系统允许与不允许之间存在明显的边界。
TypeScript不同于传统的编程语言,它可以让你自己设置类型系统的边界。这实际上是为了让你能够使用你喜欢的JavaScript,并尽可能安全地使用它。
在TypeScript中,有很多选项都可以精确地控制此边界,下文选自《深入理解TypeScript》一书,现在就让我们去了解它们吧。
▼▼▼
选项为boolean的compilerOptions,可以被指定为tsconfig.json下的compilerOptions。
1{
2 "compilerOptions": {
3 "someBooleanOption": true
4 }
5}
或者使用命令行。
1tsc --someBooleanOption
所有这些选项的默认设置都是false。
▼▼▼
有些代码无法被推断,或者推断它们可能会导致意外的错误。一个很好的例子就是函数参数,如果没有对它们进行注解,那么你将不清楚哪些是有效的。
1functionlog(someArg) {
2 sendDataToServer(someArg);
3}
4// 参数是什么,下面哪个是不正确的
5log(123);
6log('hello world');
因此,如果你没有注解函数的参数,TypeScript将会认为它是any类型的,并将继续执行。在这种情况下,将会关闭类型检查,这是JavaScript开发人员所期望的。但是这可能会让那些对安全性要求较高的人措手不及。因此,这里有一个noImplicitAny选项,当开启这个选项时,它将会标记无法被推断的类型的情况,如下所示。
1functionlog(someArg) {// 错误:someArg是any类型的
2 sendDataToServer(someArg);
3}
当然,你可以继续进行注解。
1functionlog(someArg: number) {
2 sendDataToServer(someArg);
3}
如果真的想抛弃安全性,你可以把它标记为any。
1functionlog(someArg: any) {
2 sendDataToServer(someArg);
3}
▼▼▼
在默认情况下,null和undefined可以被赋值给TypeScript中的所有类型。
1let foo: number = 123;
2foo = null; // 可以
3foo = undefined; // 可以
这顺应了大多数编写JavaScript的人的习惯。但是,同时TypeScript允许你明确指出可以分配给null/undefined的内容。
在严格的null检查模式下,null和undefined是不同的。
1let foo = undefined;
2foo = null; // 不可以
假设有一个接口Member,如下所示。
1interface Member {
2 name: string,
3 age?: number
4}
并不是所有的Member都会提供年龄,所以age是一个可选属性,也就是说age的值可能为undefined。
undefined是“万恶之源”,它通常会导致运行时错误。(编写在运行时抛出错误的代码很容易。)
1getMember()
2 .then(member: Member =>{
3 conststringifyAge = member.age.toString()
4
5 //toString属性可能undefined
6 })
但是在严格的null检查模式下,这个错误将会在编译时被捕获。
1getMember()
2 .then(member: Member =>{
3 conststringifyAge = member.age.toString() // 对象可能undefined
4 })
◆ 非空断言操作符
在一个类型检查无法得出结论的上下文中,一个新的!表达式后缀操作符,可以用来断言运算对象是非null和非undefined的,示例如下。
1 // 用--strictNullChecks进行编译
2functionvalidateEntity(e?: Entity) {
3 // 如果e是null或其他无效的实体,则抛出错误
4}
5
6functionprocessEntity(e?: Entity) {
7 validateEntity(e);
8 let a = e.name; // 错误:e可能是null
9 let b = e!.name; // 可以,我们已经断言e是非null
10}
注意,它只是一个断言,就像类型断言一样,你需要确保该值不为空。一个非null的断言实质上意味着你在告诉编译器“我知道它不是null,但是请让我使用它,即使它不是null”。
◆ 明确赋值断言操作符
TypeScript将会对类中未初始化的属性抛出错误。
1class C {
2 foo: number; // 可以,已经在构造器中初始化
3 bar: string = "hello"; // 可以,已经初始化
4 baz: boolean;
5
6// 错误:属性baz没有初始化,也没有在构造器中被赋值
7 constructor() {
8 this.foo = 42;
9 }
10}
你可以使用明确赋值断言,在属性名后加后缀,来告诉TypeScript你已经在其他地方(不是在构造器中)对它进行了初始化。
1class C {
2 foo!: number; // 注意这个感叹号
3 // 这是明确赋值断言操作符
4
5 constructor() {
6 this.initialize();
7 }
8 initialize() {
9 this.foo = 0;
10 }
11}
你也可以将此操作符与变量声明一起使用。
1let a: number[]; // 没有断言
2let b!: number[]; // 断言
3
4initialize();
5
6a.push(4); // 错误:变量在赋值之前被使用
7b.push(4); // 可以:因为被断言
8
9functioninitialize() {
10 a = [0,1,2,3];
11 b = [0,1,2,3];
12}
就像所有的断言一样,你在告诉编译器让它相信你,让编译器不再抛出错误,即使代码并没有被分配属性。
本文分享自 博文视点Broadview 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!