01 导读:Hello~TypeScript

同学你好,我是Lison。很高兴你对TypeScript感兴趣,或许你对TypeScript了解还不多,或许还有很多疑问,比如: 学 TypeScript 是不是就不需要学 JavaScript 了? Vue 用 TypeScript 改写发布 3.0 后是不是不用 TypeScript 不行? TypeScript 靠谱吗? … 诸如此类疑惑,导致你一直对它犹豫不决,那么本节我将代替 TypeScript 向你做一个自我介绍。 同学你好,我是 TypeScript,如果你觉得我是 JavaScript 的孪生兄弟,或者觉得我是前端圈新扶持起来的太子,那你可能对我是有点误解了。其实我并不是一个新的语言,用大家公认的说法,我是JavaScript的超集,你可以理解为,我是加了一身装备铭文的进化版 JavaScript。JavaScript 有的,我都有,而且做得更好。JavaScript 没有的,我也有,而且我是在很长一段时间内不会被 JavaScript 赶上的。 虽然我作为超集,但是我始终紧跟 ECMAScript 标准,所以 ES6/7/8/9 等新语法标准我都是支持的,而且我还在语言层面上,对一些语法进行拓展。比如新增了枚举(Enum)这种在一些语言中常见的数据类型,对类(Class)实现了一些ES6标准中没有确定的语法标准等等。 如果你是一个追赶技术潮流的开发者,那你应该已经将 ES6/7/8/9 语法用于开发中了。但是要想让具有新特性的代码顺利运行在非现代浏览器,需要借助Babel这种编译工具,将代码转为ES3/5版本。而我,可以完全不用 Babel,就能将你的代码编译为指定版本标准的代码。这一点,我可以说和 JavaScript 打了个平手。 另外我的优势,想必你也略有耳闻了那就是我强大的类型系统。这也是为什么造世主给我起名TypeScript。如果你是一名前端开发者,或者使用过 JavaScript 进行开发,那么你应该知道,JavaScript 是在运行的时候,才能发现一些错误的,比如: 访问了一个对象没有的属性; 调用一个函数却少传了参数; 函数的返回值是个字符串你却把它当数值用了; … 这些问题在我这里都不算事。我强大的类型系统可以在你编写代码的时候,就检测出你的这些小粗心。先来简单看下我工作的样子: interface 定义的叫接口,它定义的是对结构的描述。下面的 info 使用 ES6 的新关键字 const 定义,通过 info: Info 指定 info 要实现 Info 这个结构,那 info 必须要包含 name 和 age 这两个字段。实际代码中却只有 name 字段,所以你可以看到 info 下面被红色波浪线标记了,说明它有问题。当你把鼠标放在 info 上时,VSCode 编辑器会做出如下提示: 如果上面这个小例子中你有很多概念都不了解,没关系,Lison 在后面的章节都会讲到。...

March 13, 2024 · zlzong

02 TypeScript应该怎么学

如果你看过了本专栏的大纲,那你应该会有一种,哇,官方文档里列出的知识基本都讲了,这个专栏太细了的感觉。这一个小节我会教给大家如何去自学TypeScript。虽然你在学习本专栏的时候,Lison会手把手的带着你学习TypeScript的语法和实战。但我还是想给你讲讲如何自学TypeScript,在授你以鱼之前也会授你以渔的,这样TypeScript即使更新了,你也能毫无压力地迎接它的新特性。好,接下来让我们开始吧。 1.2.1 学会看文档 英文官方文档始终是及时更新的。但即便是官方的文档,有一些更新在更新日志里写了,而新手指南里却没有及时同步更新,所以有时看指南也会遇到困惑,就是文档里写的和你实际验证的效果不一样。遇到这种问题,首先确定你使用的TypeScript版本,然后去更新日志里根据不同版本找对这部分知识的更新记录。如果找到了,看下这是在哪个版本做的升级;如果你不放心,可以把TypeScript版本降到这个版本之前的一个版本,再验证一下。 TypeScript 是有一个中文文档的,但是这个文档只是对英文文档的翻译。官方文档中的小疏漏,这个文档也没有做校验,而且更新是有点滞后的。在写本专栏的时候,TypeScript最新发布的版本为3.4,但是中文文档还是在3.1。所以想了解TypeScript的最新动态,还是要看英文官方文档的。不过我们还是要感谢提供中文文档的译者,这对于英文不是很好的开发者帮助还是很大的。 1.2.2 学会看报错 我们在前面的例子中展示了 TypeScript 在编写代码的时候如何对错误进行提示。后面我们讲到项目搭建的时候,会使用 TSLint 对代码风格进行规范校验,根据 TSLint 配置不同,提示效果也不同。如果我们配置当书写的代码不符合规范,使用 error 级别来提示时,会和 TypeScript 编译报错一样,在问题代码下面用红色波浪线标出,鼠标放上去会有错误提示。所有如果我们使用了TSLint,遇到报错的时候,首先要区分是 TSLint 报错还是 TS 报错,来看下如何区分: 上面这个报错可以从红色方框中看到,标识了 tslint,说明它是TSLint的报错。后面括号里标的是导致这条报错的规则名,规则可以在 tslint.json 文件里配置。关于 TSLint的使用,我们会在搭建开发环境一节讲解。示例中这条报错是因为 no-console 这个规则,也就是要求代码中不能有 console 语句,但是我们在开发时使用 console 来进行调试是很常见的,所以你可以通过配置 TSLint 关闭这条规则,这样就不会报错了。但我们应该遵守规范,当我们决定引入 TSLint 的时候,就说明这个项目对代码质量有更高的要求,我们不应该在书写代码遇到TSLint报错就修改规则,而是应该根据规则去修改代码。 上面这个报错可以从红色方框中看到,标识了 ts,说明它是 TypeScript 编译器报的错误。在我们书写代码的时候,通过强类型系统,编译器可以在这个阶段就检测到我们的一些错误。后面括号里跟着的 2322 是错误代码,所有的错误代码你可以在文档的错误信息列表中查看。不过你一般并不需要去看文档,因为这里都会给你标出这个错误码对应的错误提示,而且这个错误信息根据你的编辑器语言可以提示中文错误信息。很明显这个错误是因为我们给 name 指定了类型为 string字符串 类型,而赋给它的值是123数值类型。 上面两种是在编写代码的时候就会遇到的错误提示。还有一种就是和 JavaScript 一样的,在运行时的报错,这种错误需要在浏览器控制台查看。如果你调试的是 node 服务端项目,那你要在终端查看。来看这个例子: 当我在代码中打印一个没有定义的变量时,在书写代码的时候会做提示,且当程序运行起来时,在浏览器控制台也可以看到报错。你可以打开浏览器的开发者工具(Windows系统按F12,Mac系统按control+option+i),在 Console 栏看到错误提示: 红色语句即错误信息。下面红色at后面有个文件路径main.ts,蓝色框中圈出的也是个文件路径,表示这个错误出现在哪个文件。这里是出现在main.ts中,问号后面的cd49:12表示错误代码在12行,点击这个路径即可跳到一个该文件的浏览窗口: 在这里我们就能直接看到我们的错误代码被红色波浪线标记了,这样你修改起错误来就很明确知道是哪里出错了。 1.2.3 学会看声明文件 声明文件我们会在后面讲。我们知道原来没有 TypeScript 的时候,有很多的 JS 插件和 JS 库,如果使用 TypeScript 进行开发再使用这些 JS 编写的插件和库,就得不到类型提示等特性的支持了,所以 TypeScript 支持为 JS 库添加声明文件,以此来提供声明信息。我们使用 TypeScript 编写的库和插件编译后也是 JS 文件,所以在编译的时候可以选择生成声明文件,这样再发布,使用者就依然能得到 TypeScript 特性支持。一些 JS 库的作者已经使用 TypeScript 进行了重写,有些则是提供了声明文件,一些作者没有提供声明文件的,大部分库都有社区的人为他们补充了声明文件,如果使用了自身没有提供声明文件的库时,可以使用npm install @types/{模块名}来安装,或者运用我们后面讲到的知识自行为他们补充。...

March 13, 2024 · zlzong

03 VSCode揭秘和搭建开发环境

这节课我们要做的就是在砍柴之前先磨刀,学习如何借助VSCode愉快高效地开发TypeScript项目,我们来一步一步让VSCode对TypeScript的支持更强大。如果你已经习惯了使用别的编辑器,那你也可以自行搜索下,本节课提到的内容在你使用的编辑器是否有对应的替代品。 1.3.1 安装和基本配置 如果你还没有使用过VSCode,当然先要去官网下载了,下载安装我就不多说了,安装好之后,我们先来配置几个基本的插件。 (1)汉化 如果你英语不是很好,配置中文版界面是很有必要的,安装个插件就可以了。打开VSCode之后在编辑器左侧找到这个拓展按钮,点击,然后在搜索框内搜索关键字"Chinese",这里图中第一个插件就是。直接点击install安装,安装完成后重启VSCode即可。 (2)编辑器配置 有一些编辑器相关配置,需要在项目根目录下创建一个.vscode文件夹,然后在这个文件夹创建一个settings.json文件,编辑器的配置都放在这里,并且你还需要安装一个插件EditorConfig for VS Code这样配置才会生效。配置文件里我们来看几个简单而且使用的配置: { "tslint.configFile": "./tslint.json", "tslint.autoFixOnSave": true, "editor.formatOnSave": true } tslint.configFile用来指定tslint.json文件的路径,注意这里是相对根目录的; tslint.autoFixOnSave设置为true则每次保存的时候编辑器会自动根据我们的tslint配置对不符合规范的代码进行自动修改; tslint.formatOnSave设为true则编辑器会对格式在保存的时候进行整理。 (3)TypeScript相关插件 TSLint(deprecated)是一个通过tslint.json配置在你写TypeScript代码时,对你的代码风格进行检查和提示的插件。关于TSLint的配置,我们会在后面讲解如何配置,它的错误提示效果在我们之前的例子已经展示过了。 TSLint Vue加强了对Vue中的TypeScript语法语句进行检查的能力。如果你使用TypeScript开发Vue项目,而且要使用TSLint对代码质量进行把控,那你应该需要这个插件。 (4)框架相关 如果你使用Vue进行项目开发,那Vue相关的插件也是需要的,比如Vue 2 Snippets。 Vetur插件是Vue的开发辅助工具,安装它之后会得到代码高亮、输入辅助等功能。 (5)提升开发体验 Auto Close Tag插件会自动帮你补充HTML闭合标签,比如你输完<button>的后面的尖括号后,插件会自动帮你补充</button>; Auto Rename Tag插件会在你修改HTML标签名的时候,自动帮你把它对应的闭标签同时修改掉; Bracket Pair Colorizer插件会将你的括号一对一对地用颜色进行区分,这样你就不会被多层嵌套的括号搞晕了,来看看它的样子: Guides插件能够帮你在代码缩进的地方用竖线展示出索引对应的位置,而且点击代码,它还会将统一代码块范围的代码用统一颜色竖线标出,如图: 1.3.2 常用功能 (1)终端 在VSCode中有终端窗口,点击菜单栏的【查看】-【终端】,也可以使用快捷键 ”control+`“ 打开。这样可以直接在编辑器运行启动命令,启动项目,边写代码边看报错。 (2)用户代码片段 一些经常用到的重复的代码片段,可以使用用户代码片段配置,这样每次要输入这段代码就不用一行一行敲了,直接输入几个标示性字符即可。在VSCode左下角有个设置按钮,点击之后选择【用户代码片段】,在弹出的下拉列表中可以选择【新建全局代码片段文件】,这样创建的代码片段是任何项目都可用的;可以选择【新建"项目名"文件夹的代码片段文件】,这样创建的代码片段只在当前项目可用。创建代码片段文件后它是一个类似于json的文件,文件有这样一个示例: { // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and // description....

March 13, 2024 · zlzong

04 八个JS中你见过的类型

这小节你学习起来会很轻松,这是你正式接触 TypeScript 语法的第一节课,是最最基础的语法单元。这节课我们将学习在 JavaScript 中现有的八个数据类型,当然这并不是 JavaScript 中的所有数据类型,而是现在版本的 TypeScript 支持的基本类型,在学习基础类型之前,我们先来看下如何为一个变量指定类型: 为一个变量指定类型的语法是使用"变量: 类型"的形式,如下: let num: number = 123 如果你没有为这个变量指定类型,编译器会自动根据你赋给这个变量的值来推断这个变量的类型: let num = 123 num = 'abc' // error 不能将类型“"123"”分配给类型“number” 当我们给num赋值为123但没有指定类型时,编译器推断出了num的类型为number数值类型,所以当给num再赋值为字符串"abc"时,就会报错。 这里还有一点要注意,就是number和Number的区别:TS中指定类型的时候要用number,这个是TypeScript的类型关键字。而Number为JavaScript的原生构造函数,用它来创建数值类型的值,它俩是不一样的。包括你后面见到的string、boolean等都是TypeScript的类型关键字,不是JavaScript语法,这点要区分开。接下来我们来看本节课的重点:八个JS中你见过的类型。 2.1.1 布尔类型 类型为布尔类型的变量的值只能是 true 或 false,如下: let bool: boolean = false; bool = true; bool = 123; // error 不能将类型"123"分配给类型"boolean" 当然了,赋给 bool 的值也可以是一个计算之后结果是布尔值的表达式,比如: let bool: boolean = !!0 console.log(bool) // false 2.1.2 数值类型 TypeScript 和 JavaScript 一样,所有数字都是浮点数,所以只有一个number类型,而没有int或者float类型。而且 TypeScript 还支持 ES6 中新增的二进制和八进制数字字面量,所以 TypeScript 中共支持二、八、十和十六四种进制的数值。...

March 13, 2024 · zlzong

05 TS中补充的六个类型

上个小节我们学习了八个JavaScript中常见的数据类型,你也学会了如何给一个变量指定类型。本小节我们将接触几个TypeScript中引入的新类型,这里面可能有你在其他强类型语言中见过的概念,接下来让我们一起来学习。 2.2.1 元组 元组可以看做是数组的拓展,它表示已知元素数量和类型的数组。确切地说,是已知数组中每一个位置上的元素的类型,来看例子: let tuple: [string, number, boolean]; tuple = ["a", 2, false]; tuple = [2, "a", false]; // error 不能将类型“number”分配给类型“string”。 不能将类型“string”分配给类型“number”。 tuple = ["a", 2]; // error Property '2' is missing in type '[string, number]' but required in type '[string, number, boolean]' 可以看到,上面我们定义了一个元组 tuple,它包含三个元素,且每个元素的类型是固定的。当我们为 tuple 赋值时:各个位置上的元素类型都要对应,元素个数也要一致。 我们还可以给单个元素赋值: tuple[1] = 3; 这里我们给元组 tuple 的索引为 1 即第二个元素赋值为 3,第二个元素类型为 number,我们赋值给 3,所以没有问题。 当我们访问元组中元素时,TypeScript 会对我们在元素上做的操作进行检查: tuple[0].split(":"); // right 类型"string"拥有属性"split" tuple[1].split(":"); // error 类型“number”上不存在属性“split” 上面的例子中,我们访问的 tuple 的第二个元素的元素类型为 number,而数值没有 split 方法,所以会报错。...

March 13, 2024 · zlzong

06 Symbol-ES6新基础类型

symbol是 ES6 新增的一种基本数据类型,它和 number、string、boolean、undefined 和 null 是同类型的,object 是引用类型。它用来表示独一无二的值,通过 Symbol 函数生成。 本小节代码都是纯JavaScript代码,建议在非TypeScript环境练习,你可以在浏览器开发者工具的控制台里练习。但是因为TypeScript也支持Symbol,所以如果需要特别说明的地方,我们会提示在TypeScript中需要注意的内容。 我们先来看例子: const s = Symbol(); typeof s; // 'symbol' 我们使用Symbol函数生成了一个 symbol 类型的值 s。 注意:Symbol 前面不能加new关键字,直接调用即可创建一个独一无二的 symbol 类型的值。 我们可以在使用 Symbol 方法创建 symbol 类型值的时候传入一个参数,这个参数需要是字符串的。如果传入的参数不是字符串,会先调用传入参数的 toString 方法转为字符串。先来看例子: const s1 = Symbol("lison"); const s2 = Symbol("lison"); console.log(s1 === s2); // false // 补充:这里第三行代码可能会报一个错误:This condition will always return 'false' since the types 'unique symbol' and 'unique symbol' have no overlap. // 这是因为编译器检测到这里的s1 === s2始终是false,所以编译器提醒你这代码写的多余,建议你优化。 上面这个例子中使用 Symbol 方法创建了两个 symbol 值,方法中都传入了相同的字符串’lison’,但是s1 === s2却是 false,这就是我们说的,Symbol 方法会返回一个独一无二的值,这个值和任何一个值都不等,虽然我们传入的标识字符串都是"lison",但是确实两个不同的值。...

March 13, 2024 · zlzong

07 深入学习枚举

枚举是 TypeScript 新增加的一种数据类型,这在其他很多语言中很常见,但是 JavaScript 却没有。使用枚举,我们可以给一些难以理解的常量赋予一组具有意义的直观的名字,使其更为直观,你可以理解枚举就是一个字典。枚举使用 enum 关键字定义,TypeScript 支持数字和字符串的枚举。 2.4.1. 数字枚举 我们先来通过数字枚举的简单例子,来看下枚举是做什么的: enum Status {// 这里你的TSLint可能会报一个:枚举声明只能与命名空间或其他枚举声明合并。这样的错误,这个不影响编译,声明合并的问题我们在后面的小节会讲。 Uploading, Success, Failed } console.log(Status.Uploading); // 0 console.log(Status["Success"]); // 1 console.log(Status.Failed); // 2 我们使用enum关键字定义了一个枚举值 Status,它包含三个字段,每个字段间用逗号隔开。我们使用枚举值的元素值时,就像访问对象的属性一样,你可以使用’.‘操作符和’[]‘两种形式访问里面的值,这和对象一样。 再来看输出的结果,Status.Uploading 是 0,Status['Success']是 1,Status.Failed 是 2,我们在定义枚举 Status 的时候,并没有指定索引号,是因为这是默认的编号,我们也可以自己指定: // 修改起始编号 enum Color { Red = 2, Blue, Yellow } console.log(Color.Red, Color.Blue, Color.Yellow); // 2 3 4 // 指定任意字段的索引值 enum Status { Success = 200, NotFound = 404, Error = 500 } console....

March 13, 2024 · zlzong

08 使用类型断言达到预期

学完前面的小节,你已经学习完了TypeScript的基本类型。从本小节开始,你将开始接触逻辑。在这之前,先来学习一个概念:类型断言。 虽然 TypeScript 很强大,但有时它还是不如我们了解一个值的类型,这时候我们更希望 TypeScript 不要帮我们进行类型检查,而是交给我们自己来,所以就用到了类型断言。类型断言有点像是一种类型转换,它把某个值强行指定为特定类型,我们先看个例子: const getLength = target => { if (target.length) { return target.length; } else { return target.toString().length; } }; 这个函数能够接收一个参数,并返回它的长度,我们可以传入字符串、数组或数值等类型的值。如果有 length 属性,说明参数是数组或字符串类型,如果是数值类型是没有 length 属性的,所以需要把数值类型转为字符串然后再获取 length 值。现在我们限定传入的值只能是字符串或数值类型的值: const getLength = (target: string | number): number => { if (target.length) { // error 报错信息看下方 return target.length; // error 报错信息看下方 } else { return target.toString().length; } }; 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法,所以现在加了对参数target和返回值的类型定义之后就会报错: // 类型"string | number"上不存在属性"length" // 类型"number"上不存在属性"length" 很显然,我们是要做判断的,我们判断如果 target.length 不为 undefined, 说明它是有 length 属性的,但我们的参数是string | number联合类型,所以在我们开始做判断的时候就会报错。这个时候就要用类型断言,将tagrget的类型断言成string类型。它有两种写法,一种是<type>value,一种是value as type,下面例子中我们用两种形式都写出来:...

March 13, 2024 · zlzong

09 使用接口定义几乎任意结构

本小节我们来学习接口,正如题目所说的,你可以使用接口定义几乎任意结构,本小节我们先来学习下接口的基本使用方法。 2.6.1. 基本用法 我们需要定义这样一个函数,参数是一个对象,里面包含两个字段:firstName 和 lastName,也就是英文的名和姓,然后返回一个拼接后的完整名字。来看下函数的定义: // 注:这段代码为纯JavaScript代码,请在JavaScript开发环境编写下面代码,在TypeScript环境会报一些类型错误 const getFullName = ({ firstName, lastName }) => { return `${firstName} ${lastName}`; }; 使用时传入参数: getFullName({ firstName: "Lison", lastName: "Li" }); // => 'Lison Li' 没有问题,我们得到了拼接后的完整名字,但是使用这个函数的人如果传入一些不是很理想的参数时,就会导致各种结果: getFullName(); // Uncaught TypeError: Cannot destructure property `a` of 'undefined' or 'null'. getFullName({ age: 18, phone: "13312345678" }); // 'undefined undefined' getFullName({ firstName: "Lison" }); // 'Lison undefined' 这些都是我们不想要的,在开发时难免会传入错误的参数,所以 TypeScript 能够帮我们在编译阶段就检测到这些错误。我们来完善下这个函数的定义: const getFullName = ({ firstName, lastName, }: { // 指定这个参数的类型,因为他是一个对象,所以这里来指定对象中每个字段的类型 firstName: string; // 指定属性名为firstName和lastName的字段的属性值必须为string类型 lastName: string; }) => { return `${firstName} ${lastName}`; }; 我们通过对象字面量的形式去限定我们传入的这个对象的结构,现在再来看下之前的调用会出现什么提示:...

March 13, 2024 · zlzong

10 接口的高阶用法

学习了上个小节接口的基础用法后,相信你已经能够使用接口来描述一些结构了。本小节我们来继续学习接口,学习接口的高阶用法。接口有一小部分知识与类的知识相关,所以我们放在讲解类的小节后面补充讲解,我们先来学习除了这一小部分之外剩下的接口的知识。 2.7.1 索引类型 我们可以使用接口描述索引的类型和通过索引得到的值的类型,比如一个数组[‘a’, ‘b’],数字索引0对应的通过索引得到的值为’a’。我们可以同时给索引和值都设置类型,看下面的示例: interface RoleDic { [id: number]: string; } const role1: RoleDic = { 0: "super_admin", 1: "admin" }; const role2: RoleDic = { s: "super_admin", // error 不能将类型"{ s: string; a: string; }"分配给类型"RoleDic"。 a: "admin" }; const role3: RoleDic = ["super_admin", "admin"]; 上面的例子中 role3 定义了一个数组,索引为数值类型,值为字符串类型。 你也可以给索引设置readonly,从而防止索引返回值被修改。 interface RoleDic { readonly [id: number]: string; } const role: RoleDic = { 0: "super_admin" }; role[0] = "admin"; // error 类型"RoleDic"中的索引签名仅允许读取 这里有的点需要注意,你可以设置索引类型为 number。但是这样如果你将属性名设置为字符串类型,则会报错;但是如果你设置索引类型为字符串类型,那么即便你的属性名设置的是数值类型,也没问题。因为 JS 在访问属性值的时候,如果属性名是数值类型,会先将数值类型转为字符串,然后再去访问。你可以看下这个例子:...

March 13, 2024 · zlzong