这是一篇Angular的课程文章,原文链接在此,我只做翻译。
文章看的懵懵懂懂,不是很了解在说什么,又感觉学到了点啥。
1.前言
@types
, 编译器选择类型:何时使用每种类型以及为什么?
到目前为止,Typescript
语言最与众不同的特点是其多种不同类型的类型定义。
如果你最近一直在使用Typescript
,或者没有使用Typescript
,你可能会遇到以下几个问题或错误情况。
Typescript
类型安全是否一定意味着写代码时更有仪式感?
Typescript
类型定义的多种类型是什么?
- 如何使用没有类型定义的库?
Type Definitions
和Npm
的关系是什么?
- 什么时候安装第三方类型?
- 包如何提供自己的自定义类型?
- 什么是
@types
,我什么时候应该使用它,为什么?
typings executable
和DefinitivelyTyped
是怎么回事?
- 什么是编译器选择类型,我什么时候应该使用它们,为什么?
- 为什么我有时会得到一个 "重复类型定义 "的错误,以及如何修复它?
- 为什么看起来
Promise
类型定义有时不能正确工作?
- 关于有效使用
Typescript
类型定义的结论和建议。
我们将在这篇文章中涵盖所有这些内容,我邀请你一起编写代码(从一个空的文件夹中),以获得最大的收益,因为我们将以实例驱动的方式来介绍多个概念。
这将是一个有点颠簸的旅程,但请看一下结论部分,我们将在这里总结并给出一些关于如何充分利用可用类型定义的一般指南。
这样做的好处是非常值得的,但确实需要一些时间来适应,知道在哪里可以找到正确的类型定义,知道在任何给定的时刻使用哪些类型定义。
好了,我希望你会喜欢这篇文章,所以让我们开始吧!
2.TS类型定义的方案
Typescript类型定义的多种方案是什么?
在Typescript
中,当使用Javascript
库的时候,现在基本上有4种情况下涉及到类型定义。
- 没有任何类型的定义
- 类型定义是可用的,并与编译器本身一起提供。
- 库中没有类型定义,但可以单独安装。
- 一个库内置了自己的类型定义。
那么,两者的区别是什么呢?让我们从头说起:如果根本没有类型定义呢?因为这是一个很常见的情况,而且在未来几年(如果不是永远)都会如此。
所以我们先说说这个问题:我们不能保证未来的Javascript模块会系统性地提供自己的类型,也不能保证有人会编写这些类型,发布并维护它们。
大的和最常用的模块可能会有很好的类型定义,但是小的模块呢?
3.无类型定义的库
如何使用没有类型定义的库?
我们先举一个简单的例子,让我们在一个空的文件夹中设置一个node项目,然后安装一个名为uuid
的简单模块,它可以生成唯一的标识符。
npm init
.... npm初始化的所有问题
# 在本地安装Typescript,在node_modules里面。
npm install --save-dev typescript
# 设置Typescript编译器配置文件tsconfig.json。
./node_modules/.bin/tsc --init
# 安装 uuid
npm install --save uuid
请注意,这里我们用npm init
创建了一个初始化的package.json
,并安装了一个本地版本的Typescript
。如果你用IDE
打开这个文件夹,比如Webstorm
,node_modules
里面的Typescript版本将被自动使用。
所以,你不必在全局范围内安装Typescript
,如果可能的话,最好避免在项目、命令行和IDE之间出现版本混乱。
那么现在我们已经安装了uuid
,我们如何使用它呢?
4.TS 2.1之前和之后
首先,让我们检查一下我们使用的Typescript
是哪个版本。假设我们使用的是2.1
之前的版本,比如说2.0.10
。如果我们尝试导入uuid
库会发生什么?让我们试一试。
import * as uuid from 'uuid';
console.log(uuid());
好了,那么这个导入语句是怎么回事呢?我们来分析一下。
- 我们使用ES6导入语法从一个名为
uuid
的ES6模块中导入一些东西。
- 我们说模块会有一个默认的导出,因为我们使用的是
*
。
- 我们是把那个单项导出的东西,分配给一个名为
uuid
的常量,但它会有什么类型呢?
然后我们使用uuid
,它已经隐含了类型为any
,我们使用它来调用它作为一个函数。让我们试着运行这个,看看会发生什么。
5.ts-node的使用
运行Typescript文件的简单方法
但这是一个Typescript
文件,所以我们不能在上面调用node
并运行它。我们需要一个复杂的构建系统来解决这个问题吗?不,我们可以简单地创建一个npm
脚本任务,调用tsc
编译器,然后使用node
运行输出。
但我们要保持文件系统中生成文件的干净,让我们改用一个叫ts-node
的工具。
### 安装 ts-node
npm install --save-dev ts-node
安装好ts-node
后,让我们添加一个npm
脚本任务来运行我们上面的测试程序,它将在一个名为test.ts
的文件中。
{
"name": "types-examples",
"scripts": {
"demo": "./node_modules/.bin/ts-node ./test.ts"
},
"devDependencies": {
"ts-node": "^2.0.0",
"typescript": "^2.1.0"
},
"dependencies": {
"uuid": "~3.0.1"
}
}
好了,现在我们可以用一个简单的npm
命令来运行测试程序。
npm run demo
这样一个简单的程序会有什么结果呢?这将取决于你使用的Typescript
版本,让我们来看看为什么。
6.TS2.1之前的结果
如果你使用的是Typescript 2.0.10,你会得到以下错误信息。
test.ts(3,23): error TS2307: Cannot find module 'uuid'.
那么怎么会出现编译器找不到模块的情况呢?我们知道CommonJs uuid
模块在node
模块中是有的,为什么编译器说找不到呢?
从2.0
开始,编译器应该默认在node_modules
里面找模块吧?
这里的情况是,有一个uuid
模块存在,但是uuid
没有自带类型定义。那么如何用这个版本的Typescript
使用uuid
呢?
7.缺少的Node类型定义
一种方法是尝试使用CommonJs
的require
语法,所以我们来试试。
const uuid = require('uuid');
console.log(uuid());
现在我们将在控制台中收到以下错误。
test.ts(3,14): error TS2304: Cannot find name 'require'.
这是因为Typescript
编译器不知道函数require
,所以会抛出一个错误。那么我们该如何解决这个问题呢?
8.TS 2.1和纯Js模块
我们将在本篇文章中进一步展示(如何在Typescript
程序中使用Node require
),但现在最简单的解决方案是确保我们使用Typescript 2.1
及以上版本,并再次使用导入语法。
import * as uuid from 'uuid';
console.log(uuid());
现在,一切都能按照预期工作了。在Typescript 2.1
中,如果我们在node_modules
里面有一个CommonJs
模块,但没有可用的类型定义,我们仍然可以导入它并使用它。
但是我们怎么能把它作为一个函数来使用,那uuid
是什么类型呢?
发生的情况是,这个模块被导入并隐式分配到any
类型。
9.any类型说明
any类型是如何工作的?
any
类型允许基本上绕过Typescript类型系统的类型安全。
- 我们可以使用
any
作为一个函数,并使用括号来调用它,就像我们使用uuid
一样。
- 类型为
any
的变量被假定为可能具有任何属性,就像一个纯Javascript
对象。
- 我们也可以将一个类型为
any
的变量赋值给任何其他类型的变量(而不会出现错误)。
10.any类型意义
any类型的使用意义
这意味着,虽然我们的程序编译后,我们基本上又回到了使用该库编写普通Javascript
的状态:我们不会有可靠的自动完成或重构。
但这也意味着npm
中的任何模块都可以在Typescript
程序中无缝使用。
所以这是一个很好的开始。如果其他事情失败了,我们只需要写Javascript
就可以了。但是我们如何改进这一点,让最常用的npm
库获得类型安全呢?
下一篇
TS的 @types - 类型定义完整指南 - 译文2