本篇整理自 TypeScript Handbook 中 「The Basics」 章节。
JavaScript是动态类型语言(dynamic typing),JavaScript 会在运行时先算出值的类型(type),然后再决定干什么,这需要你先运行代码然后再看会发生什么
这种行为让你很难在代码运行前(通常是指编译阶段) 就预测代码执行结果,这也意味着当你写代码的时候,你会更难知道你的代码会发生什么。
为此TypeScript使用了静态类型系统(static type system),在代码运行之前就预测需要什么样的代码。
静态类型检查(Static type-checking)
理想情况下,我们应该有一个工具可以帮助我们,在代码运行之前就找到错误。这就是静态类型检查器比如 TypeScript 做的事情
静态类型系统(Static types systems)描述了值应有的结构和行为。像 TypeScript 这样的类型检查器会利用这个信息,并且尽可能早的,在可能会出错的地方给予我们提示。
const message = "hello!";
// 此时错误会在编译阶段就被抛出,而不是在运行阶段
message(); // error
非异常失败(Non-exception 失败)
TypeScript不仅仅能识别运行时错误,也会识别出那些非异常失败
所谓运行时错误,就是 JavaScript 会在运行时告诉我们它认为的一些没有意义的事情,也就是那些在 ECMAScript 规范已经明确的声明了的错误。
而JavaScript中存在一些在JavaScript中是合法的,但是应该被识别为错误的代码,这些代码往往是产生bug的源头
const user = {
name: "Klaus",
age: 26,
}
// 在JS中,访问对象上不存在的属性会返回undefined
// 但是在TS中,这被认为是不合法的,会抛出错误
console.log(user.location)
类型工具(Types for Tooling)
TypeScript 不仅在我们犯错的时候,可以找出错误,防止我们犯错。
因为有类型信息,TypeScript还可以提供代码补全和“快速修复”功能,即自动的修复错误,重构成组织清晰的代码
tsc TypeScript 编译器(tsc,the TypeScript compiler)
# 安装 tsc —— TypeScript 编译器
npm install -g typescript
# 使用
# 运行后,会在同级目录下产生一个同名的js文件
tsc <文件名>.ts
报错时仍产出文件(Emitting with Errors)
如果我们编写的TypeScript文件中存在类型错误, 在使用了tsc编译后,依旧会发现对应的JavaScript文件仍然被编译成功。这是因为大部分时候,你要比 TypeScript 更清楚你的代码。
举个例子,假如你正在把你的代码迁移成 TypeScript,这会产生很多类型检查错误,而你不得不为类型检查器处理掉所有的错误,这时候你就要想了,明明之前的代码可以正常工作,TypeScript 为什么要阻止代码正常运行呢?
如果想要 TypeScript 更严厉一些,你可以使用 noEmitOnError 编译选项
# 此时如果TS文件中存在类型错误的时候,TS并不会将TS编译为对应的JS文件
tsc --noEmitOnError <文件名>.ts
显示类型(Explicit Types)
// string和Date就是类型注解(type annotations)
// 表明该函数,即greet函数支持传入一个 string 类型的 person 和一个 Date 类型的 date 作为函数参数
// 而所表达的含义被称之为签名 (signature)
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
添加类型注解后,TypeScript 就可以在 greet 被错误调用时提示我们:
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
// Date() 会返回一个 表示当前时间的 string 。使用 new Date() 才会产生表示当前时间的时间戳
greet("Maddison", Date()); // error
在大部分时候,我们并不需要总是写类型注解,TypeScript 可以自动推断出类型
// TS会自动识别msg的类型为string
let msg = "hello there!";
编译
function greet(person: string, date: Date) {
console.log(`Hello ${person}, today is ${date.toDateString()}!`);
}
greet("Maddison", new Date());
在被tsc编译后会产生如下代码:
"use strict";
function greet(person, date) {
console.log("Hello " + person + ", today is " + date.toDateString() + "!");
}
greet("Maddison", new Date());
注意两件事情:
- 我们的
person和date参数不再有类型注解 - 模板字符串,即用 ``` 包裹的字符串被转换为使用
+号连接
类型抹除(Erased Types)
没有任何浏览器或者运行环境可以直接运行 TypeScript 代码。
所以TypeScript 需要一个编译器,它需要将 TypeScript 代码转换为 JavaScript 代码,然后你才可以运行它。
这也就是为什么大部分 TypeScript 独有的代码会被抹除,从而避免类型注解改变程序运行时的行为。
在上面这个例子中,像我们的类型注解就全部被抹除了。
降级(Downleveling)
`Hello ${person}, today is ${date.toDateString()}!`;
经过tsc编译后
"Hello " + person + ", today is " + date.toDateString() + "!";
这是因为TypeScript默认会将新版本的代码编译为老版本的代码(在TypeScript中,默认是ES3), 以兼容各个版本的浏览器,这个行为被称之为降级(downleveling)
# 使用--target选项指定需要tsc将ts文件编译为那个版本的js文件
tsc --target es2015 <文件名>.ts
严格模式(Strictness)
默认情况下,TypeScript对于类型的检验是非常宽松的。默认情况下,类型是可选的,推断会兼容大部分的类型,对有可能是 null/ undefined 值也不做强制检查, tsc 在编译报错时依然会输出文件。
这是因为如果你正在迁移 JavaScript 代码,使用这种方式是十分有效的。
而在另一种情况下,我们希望 TypeScript 尽可能多地检查代码。为此TypeScript提供了严格模式。
不同于普通的开关严格模式。,TypeScript 提供的形式更像是一个刻度盘,你越是转动它,TypeScript 就会检查越多的内容。
如果可能的话,新项目应该始终开启这些严格设置。
noImplicitAny
在某些时候,TypeScript 并不会为我们推断类型,这时候就会回退到最宽泛的类型:any 。
但是,经常使用 any 有违背我们使用 TypeScript 的目的。你程序使用的类型越多,你在验证和工具上得到的帮助就会越多,这也意味着写代码的时候会遇到更少的 bug。
启用 noImplicitAny 配置项后,当类型被隐式推断为 any 时,会抛出一个错误。
strictNullChecks
默认情况下,像 null 和 undefined 这样的值是其它任意类型的子类型,可以赋值给其他的类型。这可以让我们更方面的写一些代码
但是忘记处理 null 和 undefined 也导致了不少的 bug,甚至有些人会称呼它为价值百万的错误!
strictNullChecks 选项会让我们更明确的处理 null 和 undefined,也会让我们免于忧虑是否忘记处理 null 和 undefined 。