TS的 @types - 类型定义完整指南 - 译文2

这是一篇Angular的课程文章,原文链接在此,我只做翻译。

文章看的懵懵懂懂,不是很了解在说什么,又感觉学到了点啥。

1.自带类型定义的依赖

类型定义和npm的关系是什么?

现在我们来介绍一下那些自带类型的模块。每天都有越来越多的模块在npm中内置了自己的类型定义。

这意味着这些类型是直接在node模块内部提供的,而不需要单独安装。

我们先举个例子,比如安装Axios同构HTTP库。如果你不知道Axios,它是一个很好的库,既可以在客户端做Ajax,也可以在服务器上做HTTP调用,同时使用同一个基于PromeAPI

npm install --save axios

这个命令将安装Axios,这样我们就可以开始使用这个客户端使用普通的Javascript来查询REST API。好消息是:我们也可以用类型安全的方式做同样的事情!这是因为Axios内置了自己的类型定义。

这是因为Axios内置了自己的类型定义。让我们来看看。

cd node_modules/axios

bash-3.2$ ls -1 *.d.ts

index.d.ts

正如我们所看到的,Axios节点模块捆绑了自己的类型定义,所以我们不需要单独安装它们! 所以让我们在我们的程序中看看这个操作。

import axios from 'axios';
import {AxiosPromise} from "axios";

const response: AxiosPromise = axios.get('/lessons', {
    ...
});

Axios库有一个默认的导出,我们已经将其导入并命名为axios。这个导入隐含了在Axios类型定义文件中声明的AxiosStatic类型。

2.变量类型的意义

我们真的需要类型注释来获得类型安全吗?

这个名为axiosimport不是any类型的,我们有自动补全以及重构和查找用处,都能正常工作。

不仅如此,你看到AxiosPromise类型注释了吗?其实这是多余的,如果我们把它删除了响应的类型,常量仍然会被推断为AxiosPromise类型,我们也会对该变量进行自动补全工作。

不仅如此,你看到url后面的配置对象了吗?它被自动推断为AxiosRequestConfig类型,所以我们也有自动补全来填写请求参数。

3.变量可选类型

在编码时类型安全并不意味着更多操作

那么为什么在这个阶段我们不会因为对象为空而出现编译错误呢?

那是因为AxiosRequestConfig类型定义只有可选的属性。通过我们的IDE,我们可以跳转到AxiosRequestConfig的定义中。

export interface AxiosRequestConfig {
  url?: string;
  method?: string;
  baseURL?: string;
  ....
}

我们可以看到,所有的属性都用一个问号标记为可选的。所以这是一个很好的例子,说明使用内置类型定义的库并不意味着我们的程序会更加啰嗦,或者不断地得到编译器错误。

4.TS最大的优点

Typescript最大的优点

使用Typescript,我们大多可以同时拥有普通Javascript的便利性和增强的工具性。如果我们使用提供自己内置类型的库,我们可以在程序中几乎到处都有自动补全、重构和查找用法,而代价只是在战略位置使用一些类型注释。

这方面最大的例外将是函数参数,编译器没有办法推断出函数参数的类型是什么。但为了文档的目的,提及我们函数参数的类型是个好主意。

5.noImplicitAny的配置

如何充分利用Typescript的类型定义

为 false 时,如果编译器无法根据变量的使用来判断类型时,将用 any 类型代替。为 true 时,进行强类型检查,会报错

如果你想最大限度地利用Typescript类型推理,并让它自动检测尽可能多的变量的类型,最好的方法是进入tsconfig.json,并将noImplicitAny属性设置为true

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "noImplicitAny": true,
        "sourceMap": false
    }
}

这样,如果由于某种原因,编译器不能推断出一个变量的类型,它就不会隐式地给它分配类型any。这可能是可用于配置编译器的最重要的属性之一。

我们几乎可以把它命名为useTypeInferenceAsMuchAsPossible而不是noImplicitAny

现在让我们利用Axios的类型定义,看看在帮助我们建立一个类型安全的程序方面,我们可以用它们来做什么。

6.Promise API

在项目中使用Promise API

我们希望能够定义一个方法,进行HTTP调用,并返回一个给定类型的Promise

import axios from 'axios';
import {AxiosPromise} from "axios";


interface Lesson {
    id:number;
    description: string;
}


function getLesson(lessonId:number): AxiosPromise<Lesson> {
    return axios.get(`lessons/${lessonId}`);
}


const promise  = getLesson(1);


promise.then(response => {
   ...
});

那么在这个小程序中,我们都做了些什么呢?我们来分析一下。

  • 我们创建了一个名为 Lesson 的自定义对象类型,有两个必选属性
  • 我们定义了一个函数getLesson,进行HTTP调用并返回一个Promise
  • 我们试图通过AxiosResponse类型中的一个通用参数来指定承诺返回的数据是什么。

所以这里的目标是在 then 子句中实现类型安全,通过隐含地知道返回的数据是 Lesson,而无需使用类型注解。

7.通用型类型

试图在promise中返回类型中使用通用型类型

那么结果是什么呢?目前我们得到两个编译器错误。

Error:(13, 38) TS2315: Type 'AxiosPromise' is not generic.
Error:(21, 14) TS7006: Parameter 'response' implicitly has an 'any' type.

那么这些错误意味着什么呢?我们来分析一下。

  • 我们不能通过一个通用参数来指定Axios承诺返回的数据。
  • 这意味着返回的数据隐含在any类型中。
  • 承诺响应也是隐式的any类型,这将引发一个错误。

在这一点上,我们可以看到,Axios附带的Promise类型定义虽然很有用,但需要一些额外的类型注释来确保我们程序的类型安全。例如:

function getLesson(lessonId:number) {
    return axios.get(`lessons/${lessonId}`);
}

const promise  = getLesson(1);

promise.then(response => {

    const lesson: Lesson = response.data;
    
    ...
    
});

而这将会很好,看看第9行我们添加了一个显式类型注解。

但此时我们可以想到Promise是一个标准的ES6 API,那么为什么不使用它呢?在我们的程序中,我们将同时受益于标准API和类型推理。

那么如果这将是一个节点程序,我们如何做到这一点呢?

8.request-promise

使用标准的Promise API编写Node程序

我们需要做的是使用另一个不一定自带承诺类型定义的库,或者在这种情况下,返回与ES6承诺兼容的类似于承诺的类型。

比如我们设置request-promise,它是流行的请求节点HTTP客户端的承诺使能器。

npm install --save request
npm install --save request-promise

那么,我们如何使用这个客户端,使用标准的承诺API来编写类型安全的节点程序呢?

9.TS中使用NodeApi

在Typescript程序中使用Node require

request-promise的工作方式是,它的APIrequest一样,但它返回的是promises。那么我们如何使用它呢?我们可以先把它作为一个普通的node模块,像这样require它。

const rp = require('request-promise');

但此时我们会得到一个错误。

Error:(3, 12) TS2304:Cannot find name 'require'.

这里发生的情况是,我们对这个名为require的全局函数没有类型定义。

Node运行时没有自带类型定义,所以我们需要单独导入这些类型。我们在哪里可以找到它们?它们也在npm中,但需要单独安装。

我们可以通过以下方式安装node运行时的类型定义。

npm install @types/node --save-dev

那么这个做了什么,这个@types模块是什么?

10.什么是@types

什么是@types,什么时候应该使用它,为什么?

在这个@types scoped包中,我们可以找到一大堆有用的类型定义,比如说node的类型定义,让我们可以使用require,比如说。

但是如果你关注Typescript有一段时间了,你可能还记得一个叫DefinitivelyTyped的东西,还有一个typings可执行文件,我们之前用它来安装类型定义。

上一篇

TS的 @types - 类型定义完整指南 - 译文1

下一篇

TS的 @types - 类型定义完整指南 - 译文3

# TS 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×