在TypeScript中,有些地方对“开箱即用”进行了限制,例如当使用一个未被声明过的变量时(当然,你可以为外部系统使用声明文件)。
也就是说,传统的编程语言在类型系统允许与不允许之间存在明显的边界。TypeScript不同于传统的编程语言,它可以让你自己设置类型系统的边界。这实际上是为了让你能够使用你喜欢的JavaScript,并尽可能安全地使用它。在TypeScript中,有很多选项都可以精确地控制此边界,现在就来看看它们吧。
boolean选项
选项为boolean的compilerOptions,可以被指定为tsconfig.json下的compilerOptions。
{
"compilerOptions": {
"someBooleanOption": true
}
}
或者使用命令行。
tsc --someBooleanOption
所有这些选项的默认设置都是false。
noImplicitAny
有些代码无法被推断,或者推断它们可能会导致意外的错误。一个很好的例子就是函数参数,如果没有对它们进行注解,那么你将不清楚哪些是有效的。
functionlog(someArg) {
sendDataToServer(someArg);
}
// 参数是什么,下面哪个是不正确的
log(123);
log('hello world');
因此,如果你没有注解函数的参数,TypeScript将会认为它是any类型的,并将继续执行。在这种情况下,将会关闭类型检查,这是JavaScript开发人员所期望的。但是这可能会让那些对安全性要求较高的人措手不及。因此,这里有一个noImplicitAny选项,当开启这个选项时,它将会标记无法被推断的类型的情况,如下所示。
functionlog(someArg) {// 错误:someArg是any类型的
sendDataToServer(someArg);
}
当然,你可以继续进行注解。
functionlog(someArg: number) {
sendDataToServer(someArg);
}
如果真的想抛弃安全性,你可以把它标记为any。
functionlog(someArg: any) {
sendDataToServer(someArg);
}
strictNullChecks
在默认情况下,null和undefined可以被赋值给TypeScript中的所有类型。
let foo: number = 123;
foo = null; // 可以
foo = undefined; // 可以
这顺应了大多数编写JavaScript的人的习惯。但是,同时TypeScript允许你明确指出可以分配给null/undefined的内容。
在严格的null检查模式下,null和undefined是不同的。
let foo = undefined;
foo = null; // 不可以
假设有一个接口Member,如下所示。
interface Member {
name: string,
age?: number
}
并不是所有的Member都会提供年龄,所以age是一个可选属性,也就是说age的值可能为undefined。
undefined是“万恶之源”,它通常会导致运行时错误。(编写在运行时抛出错误的代码很容易。)
getMember()
.then(member: Member =>{
conststringifyAge = member.age.toString()
//toString属性可能undefined
})
但是在严格的null检查模式下,这个错误将会在编译时被捕获。
getMember()
.then(member: Member =>{
conststringifyAge = member.age.toString() // 对象可能undefined
})
在一个类型检查无法得出结论的上下文中,一个新的!表达式后缀操作符,可以用来断言运算对象是非null和非undefined的,示例如下。
// 用--strictNullChecks进行编译
functionvalidateEntity(e?: Entity) {
// 如果e是null或其他无效的实体,则抛出错误
}
functionprocessEntity(e?: Entity) {
validateEntity(e);
let a = e.name; // 错误:e可能是null
let b = e!.name; // 可以,我们已经断言e是非null
}
注意,它只是一个断言,就像类型断言一样,你需要确保该值不为空。一个非null的断言实质上意味着你在告诉编译器“我知道它不是null,但是请让我使用它,即使它不是null”。
TypeScript将会对类中未初始化的属性抛出错误。
class C {
foo: number; // 可以,已经在构造器中初始化
bar: string = "hello"; // 可以,已经初始化
baz: boolean;
// 错误:属性baz没有初始化,也没有在构造器中被赋值
constructor() {
this.foo = 42;
}
}
你可以使用明确赋值断言,在属性名后加后缀,来告诉TypeScript你已经在其他地方(不是在构造器中)对它进行了初始化。
class C {
foo!: number; // 注意这个感叹号
// 这是明确赋值断言操作符
constructor() {
this.initialize();
}
initialize() {
this.foo = 0;
}
}
你也可以将此操作符与变量声明一起使用。
let a: number[]; // 没有断言
let b!: number[]; // 断言
initialize();
a.push(4); // 错误:变量在赋值之前被使用
b.push(4); // 可以:因为被断言
functioninitialize() {
a = [0,1,2,3];
b = [0,1,2,3];
}
就像所有的断言一样,你在告诉编译器让它相信你,让编译器不再抛出错误,即使代码并没有被分配属性。
内容简介:TypeScript是一种由微软开发的、开源的编程语言,近两年来发展很迅猛,已经成为很多大型项目的标配。本书涵盖了TypeScript的大部分知识点,并对其进行了细致的讲解;本书示例丰富、简单易懂,你甚至可以直接把这些例子用于自己的开发工作;本书还对TypeScript编译原理进行了深入的剖析,力争让读者知其然,并知其所以然。