TS 文档学习 --- 基础入门

216 阅读6分钟

本篇整理自 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());

注意两件事情:

  1. 我们的 persondate 参数不再有类型注解
  2. 模板字符串,即用 ``` 包裹的字符串被转换为使用 + 号连接

类型抹除(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

默认情况下,像 nullundefined 这样的值是其它任意类型的子类型,可以赋值给其他的类型。这可以让我们更方面的写一些代码

但是忘记处理 nullundefined 也导致了不少的 bug,甚至有些人会称呼它为价值百万的错误

strictNullChecks 选项会让我们更明确的处理 nullundefined,也会让我们免于忧虑是否忘记处理 nullundefined