介绍
TypeScript 是 JavaScript 语言的扩展,它使用 JavaScript 运行时和编译时类型检查器。
这种组合允许开发人员使用完整的 JavaScript 生态系统和语言功能,同时还添加可选的静态类型检查、枚举数据类型、类和接口。这些特性为开发人员提供了 JavaScript 动态特性的灵活性,但也允许更可靠的代码库,其中可以在编译时使用类型信息来检测可能在运行时导致错误或其他意外行为的问题。
额外的类型信息还提供了更好的代码库文档,并在文本编辑器中改进了 IntelliSense(代码完成、参数信息和类似的内容辅助功能)。队友可以准确地确定任何变量或函数参数的预期类型,而无需通过实现本身。
准备工作
要遵循本教程,我们将需要:
1)、一个环境,我们可以在其中执行 TypeScript 程序以跟随示例。要在本地计算机上进行设置,我们将需要以下内容。
2)、如果你不想在本地机器上创建 TypeScript 环境,你可以使用官方的 TypeScript Playground 来跟随。
3)、我们将需要足够的 JavaScript 知识,尤其是 ES6+ 语法,例如解构、rest 运算符和导入/导出。有关JavaScript的更多主题信息,建议阅读我们的 JavaScript 系列教程。
4)、本教程将参考支持 TypeScript 并显示内联错误的文本编辑器的各个方面。这不是使用 TypeScript 所必需的,但确实可以更多地利用 TypeScript 功能。为了获得这些好处,我们可以使用像 Visual Studio Code 这样的文本编辑器,它完全支持开箱即用的 TypeScript。我们也可以在 TypeScript Playground 中尝试这些好处。
本教程中显示的所有示例都是使用 TypeScript 4.2.2 版创建的。
创建自定义类型
自定义类型语法
在 TypeScript 中,创建自定义类型的语法是使用 type 关键字,后跟类型名称,然后使用类型属性分配给 {} 块。采取以下措施:
语法类似于对象文字,其中键是属性的名称,值是该属性应具有的类型。这定义了一个 Programmer 类型,它必须是一个对象,其 name 键保存一个字符串值,并且 knownFor 键保存一个字符串数组。
如前面的示例所示,我们可以使用 作为每个属性之间的分隔符。也可以使用逗号、, 或完全省略分隔符,如下所示:
使用自定义类型与使用任何基本类型相同。添加一个双冒号,然后添加我们的类型名称:
ada 常量现在将通过类型检查器而不会引发错误。
如果我们在任何完全支持 TypeScript 的编辑器中编写此示例,例如在 TypeScript Playground 中,编辑器将建议该对象期望的字段及其类型,如下面的动画所示:
如果我们使用 TSDoc 格式(一种流行的 TypeScript 注释文档样式)向字段添加注释,则在代码完成中也建议使用它们。使用以下代码并在注释中进行解释:
注释描述现在将与字段建议一起出现:
TypeScript 编译器 (tsc) 将显示错误 2322:
如果我们省略了我们的类型所需的任何属性,如下所示:
TypeScript 编译器将给出错误 2741:
添加原始类型中未指定的新属性也会导致错误:
在这种情况下,显示的错误是 2322:
嵌套自定义类型
我们还可以将自定义类型嵌套在一起。想象一下,我们有一个 Company 类型,它有一个符合 Person 类型的 manager 字段。我们可以像这样创建这些类型:
然后,我们可以像这样创建一个 Company 类型的值:
我们可以省略 manager 常量中的类型,因为它与 Person 类型具有相同的形状。当我们使用与 manager 属性类型所期望的形状相同的对象时,TypeScript 不会引发错误,即使它没有明确设置为 Person 类型。
以下不会引发错误:
我们甚至可以更进一步,直接在company对象字面量中设置manager:
所有这些场景都是有效的。
如果在支持 TypeScript 的编辑器中编写这些示例,我们会发现编辑器将使用可用的类型信息来记录自己。对于前面的示例,只要我们打开 manager 的 {} 对象文字,编辑器就会期望一个name类型的字符串属性:
现在,我们已经完成了一些使用固定数量的属性创建我们自己的自定义类型的示例,接下来,我们将尝试向我们的类型添加可选属性。
可选属性
要将可选属性添加到类型,请添加 ? 属性的修饰符。使用前面部分中的 Programmer 类型,通过添加以下突出显示的字符将 knownFor 属性转换为可选属性:
在这里我们要添加 ? 属性名称后的修饰符。这使得 TypeScript 将此属性视为可选的,并且在我们省略该属性时不会引发错误:
这将毫无错误地通过。
既然,我们已经知道如何向类型添加可选属性,那么,现在该学习如何创建一个可以容纳无限数量的字段的类型了。
可索引类型
在这里,我们使用大括号 ({}) 中的类型定义块创建一个普通类型,然后以 [key: typeOfKeys]: typeOfValues 的格式添加一个特殊属性,其中 typeOfKeys 是该对象的键应具有的类型, typeOfValues 是这些键的值应该具有的类型。
然后,我们可以像任何其他类型一样正常使用它:
使用可索引类型,我们可以分配无限数量的属性,只要它们与索引签名匹配,索引签名是用于描述可索引类型的键和值的类型的名称。在这种情况下,键具有字符串类型,值具有任何类型。
还可以将始终需要的特定属性添加到可索引类型中,就像使用普通类型一样。在以下突出显示的代码中,我们将状态属性添加到我们的数据类型:
这意味着数据类型对象必须有一个带有布尔值的状态键才能通过类型检查器。
现在,我们可以创建具有不同数量元素的对象,我们可以继续学习 TypeScript 中的数组,它可以具有自定义数量的元素或更多。
创建元素数量或更多的数组
使用 TypeScript 中可用的数组和元组基本类型,我们可以为应该具有最少元素的数组创建自定义类型。在本节中,我们将使用 TypeScript 剩余运算符...来执行此 *** 作。
想象一下,我们有一个负责合并多个字符串的函数。此函数将采用单个数组参数。这个数组必须至少有两个元素,每个元素都应该是字符串。我们可以使用以下内容创建这样的类型:
MergeStringsArray 类型利用了这样一个事实,即我们可以将 rest 运算符与数组类型一起使用,并将其结果用作元组的第三个元素。这意味着前两个字符串是必需的,但之后的其他字符串元素不是必需的。
如果一个数组的字符串元素少于两个,它将是无效的,如下所示:
TypeScript 编译器在检查此数组时将给出错误 2322:
到目前为止,我们已经从基本类型的组合中创建了自己的自定义类型。在下一节中,我们将通过将两个或多个自定义类型组合在一起来创建一个新类型。
组合类型
在这里我们将介绍两种组合类型的方法。这些将使用联合运算符传递符合一种或另一种类型的任何数据,并使用交集运算符传递满足两种类型中所有条件的数据。
Unions
unions是使用 | 创建的 (pipe) 运算符,它表示可以具有联合中任何类型的值。举个例子:
在此代码中,ProductCode 可以是字符串或数字。以下代码将通过类型检查器:
unions类型可以从任何有效 TypeScript 类型的联合中创建。
Intersections
我们可以使用相交类型来创建一个全新的类型,该类型具有相交在一起的所有类型的所有属性。
例如,假设我们有一些公共字段始终出现在 API 调用的响应中,然后是某些端点的特定字段:
在这种情况下,所有响应都将具有 status 和 isValid 属性,但只有用户响应将具有附加的用户字段。要使用交集类型创建特定 API 用户调用的结果响应,请结合使用 StatusResponse 和 GetUserResponse 类型:
ApiGetUserResponse 类型将具有 StatusResponse 中可用的所有属性以及 GetUserResponse 中可用的属性。这意味着数据只有在满足两种类型的所有条件时才会通过类型检查器。以下示例将起作用:
另一个示例是数据库客户端为包含连接的查询返回的行类型。我们将能够使用交集类型来指定此类查询的结果:
稍后,如果我们使用 fetchRowsFromDatabase() 函数,如下所示:
生成的常量joinedRows 必须有一个role 属性和一个name 属性,它们都保存字符串值,以便通过类型检查器。
使用模板字符串类型
从 TypeScript 4.1 开始,可以使用模板字符串类型创建类型。这将允许我们创建检查特定字符串格式的类型,并为我们的 TypeScript 项目添加更多自定义。
要创建模板字符串类型,我们使用的语法与创建模板字符串文字时使用的语法几乎相同。但是,我们将在字符串模板中使用其他类型而不是值。
想象一下,我们想创建一个传递所有以 get 开头的字符串的类型。我们可以使用模板字符串类型来做到这一点:
myString 将在此处通过类型检查器,因为字符串以 get 开头,然后是一个附加字符串。
如果我们将无效值传递给我们的类型,例如以下 invalidStringValue:
TypeScript 编译器会给我们错误 2322:
使用模板字符串创建类型可帮助我们根据项目的特定需求自定义类型。在下一节中,我们将尝试类型断言,它为其他无类型数据添加类型。
Using Type Assertions
如果我们想让我们的代码在这些场景中是类型安全的,我们可以使用类型断言,这是一种将变量类型更改为另一种类型的方法。通过在变量后添加 as NewType 可以实现类型断言。这会将变量的类型更改为 as 关键字之后指定的类型。
举个例子:
value 的类型为 any,但是,使用 as 关键字,此代码将 value 强制为 string 类型。
注意:要断言 TypeA 的变量具有 TypeB 类型,TypeB 必须是 TypeA 的子类型。几乎所有的 TypeScript 类型,除了 never,都是 any 的子类型,包括 unknown。
实用程序类型
在前面的部分中,我们查看了从基本类型创建自定义类型的多种方法。但有时我们不想从头开始创建一个全新的类型。有时最好使用现有类型的一些属性,甚至创建一个与另一种类型具有相同形状但所有属性都设置为可选的新类型。
使用 TypeScript 提供的现有实用程序类型,所有这些都是可能的。本节将介绍其中一些实用程序类型;有关所有可用的完整列表,请查看 TypeScript 手册的实用程序类型部分。
所有实用程序类型都是通用类型,我们可以将其视为接受其他类型作为参数的类型。可以通过使用 语法向其传递类型参数来识别通用类型。
Record
Record 实用程序类型可用于以比使用之前介绍的索引签名更简洁的方式创建可索引类型。
在我们的可索引类型示例中,我们具有以下类型:
我们可以使用 Record 实用程序类型而不是像这样的可索引类型:
Record 泛型的第一个类型参数是每个键的类型。在以下示例中,所有键都必须是字符串:
第二个类型参数是这些键的每个值的类型。以下将允许值是任何值:
Omit
Omit 实用程序类型可用于基于另一种类型创建新类型,同时排除结果类型中不需要的一些属性。
假设我们有以下类型来表示数据库中用户行的类型:
如果在我们的代码中,我们要检索除 addressId 之外的所有字段,则可以使用 Omit 创建没有该字段的新类型:
Omit 的第一个参数是新类型所基于的类型。第二个是我们要省略的字段。
如果我们在代码编辑器中将鼠标悬停在 UserRowWithoutAddressId 上,我们会发现它具有 UserRow 类型的所有属性,但我们省略了这些属性。
我们可以使用字符串联合将多个字段传递给第二个类型参数。假设我们还想省略 id 字段,我们可以这样做:
Pick
Pick 实用程序类型与 Omit 类型完全相反。我们无需说出要省略的字段,而是指定要从其他类型使用的字段。
使用我们之前使用的相同 UserRow:
假设我们只需要从数据库行中选择电子邮件键。我们可以像这样使用 Pick 创建这样的类型:
Pick 这里的第一个参数指定了新类型所基于的类型。第二个是我们想要包含的键。
这将等同于以下内容:
我们还可以使用字符串联合来选择多个字段:
Partial
使用相同的 UserRow 示例,假设我们想创建一个新类型,该类型与我们的数据库客户端可以用来将新数据插入用户表中的对象相匹配,但有一个小细节:我们的数据库具有所有字段的默认值,所以,我们是不需要通过其中任何一个。
为此,我们可以使用 Partial 实用程序类型来选择性地包括基本类型的所有字段。
我们现有的类型 UserRow 具有所需的所有属性:
要创建所有属性都是可选的新类型,我们可以使用 Partial 实用程序类型,如下所示:
这与拥有这样的 UserRowInsert 完全相同:
实用程序类型是一个很好的资源,因为它们提供了一种比从 TypeScript 中的基本类型创建类型更快的方法来构建类型。
总结
创建我们自己的自定义类型来表示我们自己的代码中使用的数据结构,可以为我们的项目提供灵活且有用的 TypeScript 解决方案。除了从整体上提高我们自己代码的类型安全性之外,将我们自己的业务对象类型化为代码中的数据结构将增加代码库的整体文档,并在与团队成员一起工作时改善我们自己的开发人员体验相同的代码库。
需要先自行安装npm
手动创建一个如下结构文件目录(nodejs工程)用来运行electron,如下图:
命令行模式下,切换到工程根目录(这里就是test目录)
安装typescript:
安装electron:
自行建立上图中的各个文件 ,部分文件内容如下:
tsconfig.json
tsconfig更多具体配置参考官方文档:
https://www.tslang.cn/docs/handbook/compiler-options.html
package.json
"main": "./build/app.js" : 表示程序主入口是build目录下的app.js, app.js文件由是由tsc编译src/app.ts得到
"prestart":"tsc" : 表示在执行npm start之前先执行tsc进行当前工程目录的ts文件编译工作.
更多具体配置参考官方文档:
https://docs.npmjs.com/files/package.json
views/css目录和views/js目录 是具体业务逻辑:
index.html:
app.ts:
ClassA.ts:
在命令行模式中执行
显示结果:
Typescript 是 javascript 的类型超集,旨在简化大型 JavaScript 应用程序的开发。Typescript 加入了常见的概念例如 类(classes),泛型(generics),接口(interfaces)和静态类型(static types)并允许开发人员使用静态检查和代码重构等工具。
为什么在意 Typescript
现在问题仍然是为什么你应该优选使用 Typescript。这有一些关于为什么 javascript 开发者应该考虑学习 Typescript 的原因。
静态类型
Javascript 是动态类型的,这意味着直到在运行时实例化时,它不知道变量的类型,这可能导致项目中的问题和错误。Typescript 加入了对 Javascript 静态类型支持如果你正确的使用它处理由变量类型的错误设定引起的错误。您仍然可以完全控制输入代码的严格程度,或者甚至根本不使用类型。
更好的 IDE 支持
Typescript 相比 Javascript 一个更大的优势是更好的 IED 支持包括了来自 Typescript 编译器智能,实时的提示,调试以及更多功能。这里还有一大堆扩展进一步 提升你的 Typescript 开发体验。
应用新的 ECMAScript 特性
Typescript 使您可以使用最新的 ECMAScript 功能,并将它们转换到您选择的 ECMAScript 目标。这意味着您可以使用最新的工具开发应用程序,而无需担心浏览器支持。
什么时候你该使用它
到目前为止,我们应该知道为什么 Typescript 是有用的以及如何改善我们的开发体验。但它并不是解决所有问题的方法,当然也不能阻止你自己编写可怕的代码。那么让我们来看看你应该在哪里使用 Typescript。
当你拥有一个很大的代码库时
Typescript 是大型代码库的一个很好的补充,因为它可以帮助您防止许多常见错误。这尤其适用于多个开发人员工作在同一项目之中。
当你项目成员早已知道静态类型语言时
另一个明显使用 Typescript 的场景是当你和你的团队已经知道静态类型的语言像 Java 和 C# 不想改为编写 Javascript。
设置/建立
要设置 typescript,我们只需要使用 npm 包管理器安装它并创建一个新的 Typescript 文件。
安装完成之后我们可以继续探寻 Typescript 提供给我们的语法和功能特性。
类型
现在让我们来看看 Typescript 所提供的类型:
数值(Number)
Typescript 所有的值类型都是浮点数。所有的数字包括二进制和十六进制都是数值类型。
字符串(String)
与其他语言一样,Typescript 使用 String 数据类型来保存文本数据。
你还可以用反引号来应用多行字符串并嵌入表达式。
布尔类型(Boolean)
Typescript 支持所有的基本数据类型,布尔类型,值必须为 true 或者 false。
指定类型
现在我们已经有了基本的数据类型,我们可以看看你如何在 Typescript 中指定类型。基本上,您只需要在名称和冒号后面写出变量的类型。
单一类型
这里例子为我们如何为变量指定字符串数据类型
所有其他数据类型也是这样使用。
多类型
你仍然可以通过| *** 作符为你的变量指定多个数据类型:
这里我们使用|为变量分配两种类型。现在我们可以在其中存储字符串和数值。
类型检测
现在让我们看看我们如何检查我们的变量是否具有正确的类型。我们有多种选择,但在这里我只展示了两个最常用的选项。
Typeof
typeof仅仅知道基本类型。这意味着它只能检查变量是否是我们上面定义的数据类型之一。
在此示例中,我们创建一个字符串类型变量并使用 typeof 命令检查 str 是否为 Number 类型(始终为 false)。然后我们打印是否是数值。
Instanceof
instanceof 运算符与 typeof 几乎相同,只是它还可以检查 javascript 尚未定义的自定义类型。
在这里,我们创建一个自定义类型,我们稍后将在本文中讨论,然后创建它的实例。之后,我们检查它是否真的是 Human 类型的变量,如果是,则在控制台中打印。
类型断言
有时我们还需要将变量转换为特定的数据类型。这经常发生在你已经指定了一个泛型类型像 any 并且你想使用它具体的类型的方法。
有很多选择可以解决这个问题,但在这里我只分享其中两个。
As 关键字
通过在变量名之后使用 as 关键字跟随具体的数据类型来转换变量的类型。
这里我们将 str 变量转换为字符串,以便我们可以使用 length 属性(如果您的 TSLINT 设置允许,甚至可以在没有转换的情况下工作)。
> *** 作符
我们也可以使用> 运算符,它与 as 关键字具有完全相同的效果,只有语法差异。
此代码块与上面的代码块具有完全相同的功能。它只是语法不同。
数组
Typescript 中的数组是相同对象的集合,可以用两种不同的方式创建。
创建数组
使用 []
我们可以通过指定类型后跟[]来定义数组对象,以表示它是一个数组。
在这个例子中,我们创建一个字符串数组,它包含三个不同的字符串值。
使用泛型数组
我们还可用指定 Array 定义泛型数组
这里我们创建一个数值数组,它包含 5 个不同的数字。
多(混合)类型数组
此外,我们还可以使用| *** 作符将多个类型分配给单个数组。
此例中我们创建了一个数值可以包含字符串和数值。
多维数组
Typescript 还允许我们定义多维数组,这意味着我们可以将数组保存在另一个数组中。我们可以通过使用多个[]运算符来创建一个多维数组。
这里我们创建一个包含另一个数字数组的数组。
元组(Tupels)
元组基本类似数组但有一点不同。我们可以定义每个位子上储存数据的类型。这意味着我们可以通过方括号内的枚举来限制固定索引位置的类型。
在此列中,我们定义了一个简单的元组,在索引 0 位置上指定为数值类型,在索引为 1 位置上指定为字符串类型。这意味着如果我们尝试在此索引上放置另一种数据类型,则会抛出错误。
以下是非法元组的示例:
枚举(Enums)
与大多数其他面向对象编程语言一样,Typescript 中的枚举允许我们定义一组命名常量。 Typescript 还提供基于数值和基于字符串的枚举。使用 enum 关键字定义 Typescript 中的枚举。
数值枚举
首先,我们将查看数值枚举,其中我们将键值与索引匹配。
上面,我们定义了数值枚举将 Playing 初始化为 0,Paused 为 1 等等。
我们也可以将初始化器留空,而 Typescript 会从零开始自动索引它。
字符串枚举
定义字符串枚举也十分简单,我们只需要在定义的每个枚举值后初始化字符串值。
这里我们通过使用字符串初始化我们的状态来定义字符串枚举。
对象(Objects)
Typescript 中的对象是包含一组键值对的实例。这些值可以是变量,数组甚至函数。它也被视为表示非基本类型的数据类型。
我们可以使用大括号创建一个对象:
这里我们创建了一个 human 对象包含三个不同的键值对。
我们可以为对象加入方法:
自定义类型
Typescript 还允许我们自定义类型,以便于我们后续重用。要创建自定义类型,我们只需要使用type关键字并定义我们的类型。
在此示例中,我们定义了一个名为 Human 包含三个属性的自定义类型。现在让我们看看如何创建这种类型的对象。
在这里,我们创建自定义类型的实例并设置所需的属性。
方法参数和返回类型
Typescript 允许我们为方法参数和返回值指定数据类型。现在让我们看一下使用 Typescript 定义函数的语法。
这里我们有两个示例函数,它们都具有定义类型的参数。我们还看到在结束括号后定义返回类型。
现在我们可以像普通的 javascript 一样调用我们的函数,但编译器会检查我们是否为函数提供了正确的参数。
可选属性
Typescript 允许我们为方法(注:接口等同样可以定义可选属性)定义可选属性。我们通过 ? *** 作符定义。
在这个例子中,lastName 是一个可选参数,这意味着当我们不提供调用函数时,我们不会从编译器中获得错误。
这表示 2 个示例都被视为正确的。
默认值
我们使用可选属性的第二种方法是为它指定一个默认值。我们可以通过直接在函数头部赋值来实现。
在此例我中我们 lastName 赋予了默认值这意味着我们不必每次调用方法时提供它。
接口(Interfaces)
让我们看个例子让定义更加清晰:
可选属性
在 Typescript 中,有时并不是所有接口属性都是必需的。可以使用 ? 运算符在属性后面将其设置为可选。
在这里,我们创建一个具有一个普通和一个可选属性的接口,该属性是使用 ? 运算符。这就是我们两个人初始化都有效的原因。
只读属性
我们的接口中一些属性也应该只在首次创建对象时修改赋值。我们可以通过将 readonly 关键字放在我们的属性名称之前来指定此功能。
在此示例中,id 属性是只读的,在创建对象后无法更改。
模块(Barrels Modules)
Barrels 允许我们在一个更方便的模块中汇总多个导出模块。
我们仅需要创建一个新文件,它将导出我们项目中的多个模块 (译者注:根据 ECMAScript 定义一个文件定义一个模块,此处可能表示模块聚合(类似库等的入口文件))。
之后我们可以通过这个便利的单一导入语句引入这些模块。
泛型(Generics)
泛型允许我们创建兼容广泛类型而不是单一类型的组件。这使得我们的组件“ 开放”和复用。
现在您可能想知道为什么我们不只是使用任何(any)类型来使组件接受多种类型而不是单一类型。让我们看一个例子更好地了解。
我们想要一个简单的假函数(dummy function),它返回传入的参数:
然而 any 是通用的,某种程度它能接受所有类型参数但有一个很大的区别。我们丢失了我们传入的参数是什么类型以及返回值是什么类型。
所以让我们来看看,我们如何接受所有类型并知道它返回值的类型。
这里我们使用泛型参数 T,因此我们可以捕获变量类型并在以后使用它。我们还使用它作为返回参数类型,它允许我们在检查代码时看到相应的类型。
更多详细介绍你可以查看Charly Poly关于Generics and overloads的文章
访问修饰符(Access Modifiers)
访问修饰符控制我们类成员的可访问性。 Typescript 支持三种访问修饰符 - 公共的(public),私有的(private)和受保护的(protected)。
公共的
公共成员可以在任何地方访问,没有任何限制 这也是标准修饰符,这意味着您不需要使用 public 关键字为变量添加前缀。
私有的
私有成员只能在其定义的类中能访问。
受保护的
保护成员只能在其定义的类及其子类中访问。
TSLINT
TSLINT 是 Typescript 的标准 linter,可以帮助我们编写干净,可维护和可读的代码。它可以使用我们自己的 lint 规则,配置和格式化程序进行自定义。
设置
首先我们需要安装 Typescript 和 tslint,我们可以全局安装和局部安装:
之后,我们可以使用 TSLINT CLI 在我们的项目中初始化 TSLINT。
现在我们有了 tslint.json 文件,我们可以开始配置我们的规则了。
配置
TSLINT 允许使用配置我们自己的规则并自定义代码的外观。默认情况下,tslint.json 文件看起来像这样,只使用默认规则。
我们可以通过将它们放在 rules 对象中来添加其他规则。
有关所有可用规则的 概述,您可以查看官方文档。
结论
恭喜您一路走到最后!希望此篇文章帮助您理解 Typescript 的基础知识以及如何在项目中使用。
如果您发现这个有用,请考虑推荐并与其他开发人员共享。也可以访问我的网站学习更多。https://www.icoderoad.com
如果您有任何问题和反馈,在以下评论中让我知道。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)