1-1. 自我介绍和课程结构介绍
前言
任务列表
- 了解课程结构(也就是该教程袁进老师计划如何讲解)
- 了解一下学习 TS 的必要性
notes
袁进老师的简单自我介绍
在程序员这一行混了 10 年,其中 4 年左右在搞开发,其中有 3 年时间做的是技术总监,然后转向了教育行业,干了 5 年,大致带了 1000 多位的学生。先后教过 .NET、JAVA、PHP,这 18、19 年,主要在做前端方向。
课程结构
基础 + 进阶 + 实战。
- 基础:比较简单,讲解的是 TypeScript 中最为核心的部分。
- 进阶:对基础部分涉及到的相关知识点进一步讲解。
- 实战:每个部分的知识点讲解完之后,都会有对应的项目实战。
基础阶段仅介绍必须要掌握的知识点,在进阶阶段还会对 TypeScript 的内容进行一些扩展。
Q & A
🤔 为什么要学习 TypeScript?
- 就业时,获得更大的竞争优势
- 开发时,在开发时获得更好地开发体验,解决 JS 中一些难以处理的问题
该 typescript 课程开始录制的时间是在 2019 年 4 月 23 日,笔记记录的时间是在 2022 年 6 月 10 号。
袁老当时在视频中,猜测 ts 这玩意儿,接下来几年很可能会火,结果确实如此。
JS 中都有哪些难以解决的问题,在下一节 1-2 中会介绍到。
1-2. JS 语言的问题
前言
任务列表
- 认识 JS 中的一些常见错误
- 理解为什么 JS 没法帮我们提前识别出这些错误
- 理解 JS 的初衷
- 理解 JS 的特点
课程内容概述
本节课介绍的内容,主要是为了让我们了解到在使用 js 做开发时所带来的一些常见问题,而这些问题,js 自身没法解决。这正是为什么要学习 typescript 的原因。
参考资料
18 年统计的数据:从 1000 个 JavaScript 的项目中,提取出的最为常见的报错。
notes
JS开发中的常见问题
- 使用了不存在的变量、函数、成员
- 把一个不确定的类型当做一个确定的类型处理
- 使用了 null 或 undefined 中的成员
这些问题,我们在开发时,多多少少都会遇到。但是,解决这些 bug,对于我们开发而言,并没有任何实质性的帮助,都是一些很低级的错误。
我们希望的当这样的错误发生时,能有东西提醒我们出错了,这样就能大大减少我们 debug 的时间,改善开发体验。下面我们要学习的 typescript 就是用于解决该问题的。
JS的原罪
JS 语言本身的特性,决定了它无法适应大型项目,因为:
- 弱类型,某个变量,可以随时更换类型
- 解释型,看一行执行一行(错误发生的时间,是在运行时)
JS 的设计初衷
JS 开发出来,也就花费了两周时间。它设计出来的初衷,就是为了解决一些浏览器上的简单交互需求。压根就没指望 JS 能用来写什么大型项目,所以将其设计为一门「弱类型」、「解释型」的语言,就当时的需求场景来说,是非常合理的。
codes
写一个 demo,要求实现的效果:
- 输入:
da hu you
- 输出:
Da Hu You
function getUserName(){
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
function parseUserName(myname){
return myname.split(" ")
.filter(it => it)
.map(it => it[0].touppercase() + it.subStr(1))
.join(" ");
}
let mynema = getUserName();
parseUsername(myname); // 获取解析后的结果
上面这段程序中存在很多错误,下面逐一分析:
- 变量名写错:
myname
、mynema
- 函数名写错:
parseUserName
、parseUsername
- 字符串原型方法写错
touppercase
❌、toUpperCase
✅subStr
❌、substr
✅
- getUserName 返回的不一定是字符串,也有可能是数字
Q & A
🤔 为啥这些明显的错误,JS 无法识别出来呢? 首先我们要知道,我们写的代码最终的运行环境是在宿主环境(比如“浏览器”)中的。
在宿主环境中,有可能会嵌入一些 script,我们不知道嵌入的 script 中是否会包含:
- myname 变量
- parseUsername 方法
- 扩展了字符串原型 String.prototype.touppercase、String.prototype.subStr
也就是说,咋们上述代码在执行的时候,有可能是没问题的。。。
🤔 为什么很多情况下,我们犯了一些很低级的错误,JS 无法识别出来呢?
var a = 123;
// ... 假设这里很多代码
a = "123";
// ... 假设这里很多代码
a.substr(1);
举个例子,有一个变量 a,在声明它时是 number
类型,但是我们后面却尝试去调用字符串的 api。
对于这种情况,我们希望看到的是:在我们编写代码时,就提供 ⚠️ 警告,尽快将这种错误解决掉。
然而,JS 做不到,因为:
- 由于 JS 是弱类型的,当一个变量声明后,可以随时变更类型,它不知道我们从声明变量 a 到调用变量 a.substr 的过程中,是否中途又给变量 a 重新赋值了。中途给变量 a 重新赋值的这种情况是有可能发生的,这样 JS 就无法确定 a.substr 这么写是否是错误的。
- 由于 JS 是解释型的,错误发生的时间是在运行时,如果程序中发生了错误,只要不是低级的语法上的错误,那么这些错误都将在运行时才会抛出。所以,在将代码丢给宿主环境执行之前,它并不知道 a.substr 这么写是否是错误的。
前端开发中,大部分的时间都是在排错。项目的规模越大,这些问题就越明显。但是,这些问题在其他语言,比如 Java 中,是不存在的,一旦我们犯了类似上述这样的错误,它就会提醒我们出错了,但是在 js 中,它就没法提醒我们发生了错误。由于 js 是弱类型,解释性的语言,所以,这些问题是 js 无法解决的。
这就导致了一个现象:在前端开发中,大部分的时间都是在排错。而 TS 的出现,就是为了弥补 JS 在这方面的一些不足,改善我们的开发体验,降低我们的开发成本(尤其是 debug)。
1-3. TS 语言的特点
前言
任务列表
- 理解 TS 语言的特点
- 是 JS 的超集
- 是可选的
- 是静态的
- 理解静态(强类型)语言和动态(弱类型)语言之间的区别
参考资料
这是 TS 的官方文档,在后续学习过程中遇到的任何问题,都可以在这里边查找。
这个中文网是个人翻译的,里边有一些翻译不是很准确,若有阅读外文文档的能力,最好还是去看官网。
上网找了篇文章,介绍静态语言和动态语言之间的区别,这篇文章比较精简,还算不错。
notes
What is TypeScript?
TS 的定义:
- TypeScript 是 JS 的超集,是一个可选的、静态的类型系统。(袁老给的定义)
- TypeScript 是面向对象的 JavaScript。
- TypeScript 是 JavaScript 的超集,具有可选的类型并可以编译为纯 JavaScript。
- TypeScript is a strongly typed programming language that builds on JavaScript, giving you better tooling at any scale.
译:TypeScript 是一种基于 JavaScript 的强类型编程语言,它可以在任意规模的项目中,为您提供更好的工具。
网上对 typescript 是什么的定义有很多,以上记录的几点是比较常见的一些定义。其中最后一个是来自 ts官方 的定义。
翻译:
- TypeScript 向 JavaScript 添加了额外的语法,以支持与您的编辑器更紧密的集成。在编辑器中尽早发现错误。
- TypeScript 代码转换为 JavaScript,只要是 JavaScript 可以运行的地方,它都能运行:在浏览器中、Node.js 或 Deno 中以及您的应用程序中。
- TypeScript 能够识别 JavaScript,并使用类型推断为您提供出色的工具,而无需额外的代码。
最后一点所说的 understands「理解」表示的含义是:我们直接将 .js 文件改为 .ts 文件,ts 也能够认识它。也就是说,在 .ts 文件中,我们可以完全将其视作一个 .js 文件来写。对于 TS 给我们提供的类型系统,我们可以选择是否使用。
注解:
超集
这是数学中的概念,可理解为 ts 包含 js。
无论是浏览器环境,还是 node 环境,它们都能直接识别 js 代码,但是无法直接识别 ts 代码
我们之前写的所有 js 代码,都可以直接丢到 .ts 中执行,不过 .ts 中的内容想要执行,还得先编译为 js 代码才行
可选
表示我们在写 ts 代码时,完全可以按照之前写 js 代码的形式来写
ts 给我们提供的功能,我们可以选用
当然,不用也是完全 OK 的,并不会报错
静态
指变量的数据类型不可变,这一点和 js 是完全不同的。
类型检查发生的时间,是在编译的时候,而非代码运行的时候,TS 不参与任何运行时的类型检查。
类型系统
对代码中所有的标识符(变量、函数、参数、返回值)进行类型检查。类型检查发生的时间,是在编译的时候,而非代码运行的时候。TS 不参与任何运行时的类型检查。
类型推断
type interface,可以理解为 typescript 能够智能地判断我们所写的标识符的类型,检查类似于拼写错误等低级问题,让我们尽早发现隐患代码。
图片中的 Types 表示的就是 typescript 的类型系统,也就是在 JS 基础上新增的东西,也正是我们所要学习的重点。
有关 TS 的一些常识:
- TS 是微软在 2012 年发布的。
- 由 Anders Hejlsberg 负责开发 TS 项目。
- 开源、拥抱 ES 标准。
- 版本:4.7(当前时间 2022年6月11日 的最新版)
- 视频录制时是 19 年 4 月,当时版本是 3.4。
- 官网:http://www.typescriptlang.org/
中文网:https://www.tslang.cn/
这个中文网是个人翻译的,里边有一些翻译不是很准确,若有阅读外文文档的能力,最好还是去看官网。
Globally Installing TypeScript
You can use npm to install TypeScript globally, this means that you can use the tsc
command anywhere in your terminal.
npm install -g typescript
what is **tsc**
?
tsc 的全称为 typescript compiler,它是 typescript 的编译器,可以将我们写的 ts 代码给转换为 js 代码。
how to use **tsc**
?
tsc + 空格 + 被编译的 .js 文件的相对路径
tsc 1.ts
# 使用 tsc 命令,将当前目录的 1.ts 文件给编译为 1.js
完善了 js 的面向对象开发
ts 的类型系统弥补了 js 在类型检查方面的诸多不足,它出现增强了 js 面向对象的开发。
js 中也有类和对象,它本身也是支持面向对象的开发方式的,但是由于 js 自身是没有类型检查的,这就导致很多面向对象的场景实现起来会出现诸多问题。
所以,直接使用 js 实现面向对象,还不是很完善。
但是,ts 出现后,我们就可以利用它编写出更加完善的面向对象的代码。
codes
TypeScript 能够识别 JavaScript,并使用类型推断为您提供出色的工具,而无需额外的代码。
下面以一段问题诸多的 .js 代码文件为例,来简单认识一下 typescript 都能够智能地识别出哪些错误。
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUsername();
mynema = myname.split(" ")
.filter(it => it)
.map(it => it[0].touppercase() + it.subStr(1))
.join("");
我们只要将文件 1.js 重命名为 1.ts。
会发现它会将我们代码中,这些明显出错的地方标注出来。如下图所示:
// ts 提示我们函数名写错了,修改一下函数名:
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
mynema = myname.split(" ")
.filter(it => it)
.map(it => it[0].touppercase() + it.subStr(1))
.join("");
修改好函数名后,我们再来看 ts 帮我们定位到的下一个问题:
// 修改变量名
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
myname = myname.split(" ")
.filter(it => it)
.map(it => it[0].touppercase() + it.subStr(1))
.join("");
ts 告诉我们 myname 的类型有可能是 string | number,但是 number 类型上不存在 split 方法。
我们只要在执行这段程序之前做一个类型检测即可:
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
if (typeof myname === "string") {
myname = myname.split(" ")
.filter(it => it)
.map(it => it[0].touppercase() + it.subStr(1))
.join("");
}
// 修改方法名
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
if (typeof myname === "string") {
myname = myname.split(" ")
.filter(it => it)
.map(it => it[0].toUpperCase() + it.subStr(1))
.join("");
}
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
if (typeof myname === "string") {
myname = myname.split(" ")
.filter(it => it)
.map(it => it[0].toUpperCase() + it.substr(1))
.join("");
}
function getUserName() {
if (Math.random() < 0.5) {
return "da hu you";
}
return 404;
}
let myname = getUserName();
if (typeof myname === "string") {
myname = myname
.split(" ")
.filter((it) => it)
.map((it) => it[0].toUpperCase() + it.substr(1))
.join("");
}
小结
我们仅仅是将后缀名改为 .ts
,它就能帮我们定位到这些低级的错误。
好让我们提前将这些错误给排除,单单是这一点功能,我们就有学习 ts 的必要。
编译1.ts 文件,tsc 1.ts
我们会发现,这样编译之后,原来的 1.ts 文件会出现报错信息,暂且先别管这些错误信息,以及编辑后生成的 1.js 文件的内容,后边会介绍为什么。「2-1 中会介绍」
Q & A
🤔 TS 是 JS 的超集,那么 TS 相对于我们之前所学习的 JS,都新增了啥?
其实就是新增了一个类型系统,并没有扩展 JS 的某些功能。
🤔 原先使用 JS 没法做的事儿,有了 TS 之后是不是就能做了? 这种说法是错误的
虽说 TS 是 JS 的超集,但它仅仅是扩展了 JS 的类型系统,压根就没有提供啥新的功能。
这就意味着,从功能实现上讲,并不存在有什么事儿,是 JS 没法做,TS 才可以做的。
并且要知道一点,我们写的 TS 是要进行编译的,最终运行的依旧是编译后生成的 JS 文件。
🤔 若我们将一个不确定的类型视作一个确定的类型来处理,那么 TS 会报错,此时该如何处理? 在 ts 文件中,如果将一个不确定的类型视作一个确定的类型来用,是会报错的。
function test() {
return Math.random() > 0.5 ? 1.23 : "dahuyou";
}
const a = test(); // test 可能会返回 number | string
// 直接这么写都是错误的,因为这么做,相当于将一个不确定的类型视作一个确定的类型来处理
a.toUpperCase(); // ×
Math.round(a); // ×
function test() {
return Math.random() > 0.5 ? 1.23 : "dahuyou";
}
const a = test(); // test 可能会返回 number | string
if (typeof a === "string") {
// 若代码执行到这里,说明此时 a 一定是一个 string 类型
console.log("获取到的是一个字符串", a.toUpperCase());
} else {
// a 一定是一个 number 类型
console.log("获取到的是一个数字", Math.round(a));
}
就目前所学的内容来看,我们可以使用 typeof 关键字来识别变量的类型。当变量的类型确定后,我们再去访问对应的 api 就不会再报错啦。
在学习了更多有关 TS 的内容后,还会有更多的解决方案。
🤔 静态语言和动态语言之间的区别?
- 静态(强类型)
- 变量在声明的时候,强制要求必须明确类型
- 变量的类型一旦确定,后续将无法变更
- 变量的数据类型,在编译时确定
- 动态(弱类型)
- 变量的类型是动态的,可变的
- 变量的数据类型是不确定的
- 变量的数据类型,在程序执行时才能确定