typescript - 一种思惟方式

电影《降临》中有一个观点,语言会影响人的思惟方式,对于前端工程师来讲,使用 typescript 开发无疑就是在尝试换一种思惟方式作事情。javascript

其实直到最近,我才开始系统的学习 typescript ,先后大概花了一个月左右的时间。在这以前,我也在一些项目中模仿他人的写法用过 TS,不过平心而论,在这一轮系统的学习以前,我并不理解 TS。一个多月前,我理解的 TS 是一种能够对类型进行约束的工具,可是如今才发现 TS 并不简单是一个工具,使用它,会影响我写代码时的思考方式。html

TS 怎么影响了个人思考方式

对前端开发者来讲,TS 能强化了「面向接口编程」这一理念。咱们知道稍微复杂一点的程序都离不开不一样模块间的配合,不一样模块的功能理应是更为清晰的,TS 能帮咱们梳理清不一样的接口。前端

明确的模块抽象过程

TS 对个人思考方式的影响之一在于,我如今会把考虑抽象和拓展看做写一个模块前的必备环节了。固然一个好的开发者用任何语言写程序,考虑抽象和拓展都会是一个必备环节,不过若是你在平常生活中使用过清单,你就会明白 TS 经过接口将这种抽象明确为具体的内容的意义所在了,任何没有被明确的内容,其实都有点像是可选的内容,每每就容易被忽略。java

举例来讲,好比说咱们用 TS 定义一个函数,TS 会要求咱们对函数的参数及返回值有一个明确的定义,简单的定义一些类型,却能帮助咱们定位函数的做用,好比说咱们设置其返回值类型为 void ,就明确的代表了咱们想利用这个函数的反作用;node

把抽象明确下来,对后续代码的修改也很是有意义,咱们不用再担忧忘记了以前是怎么构想的呢,对多人协做的团队来讲,这一点也许更为重要。react

固然使用 jsdoc 等工具也能把对函数的抽象明确下来,不过并无那么强制,因此效果不必定会很好,不过 jsdoc 反而能够作为 TS 的一种补充。

更自信的写代码

TS 还能让我更自信的写前端代码,这种自信来自 TS 能够帮咱们避免不少可能因为本身的忽略形成的 bug。实际上,关于 TS 辅助避免 bug 方面存在专门的研究,一篇名为To Type or Not to Type: Quantifying Detectable Bugs in JavaScript 的论文,代表使用 TS 进行静态类型检查能帮咱们至少减小 15% 以上的 bug (这篇论文的研究过程也颇有意思,感兴趣能够点击连接阅读)。git

能够举一个例子来讲明,TS 是怎么给我带来这种自信的。github

下面这条语句,你们都很熟悉,是 DOM 提供依据 id 获取元素的方法。typescript

const a = document.getElementById("a")

对我本身来讲,使用 TS 以前,我忽略了document.getElementById的返回值还多是 null,这种不经意的忽略也许在将来就会形成一个意想不到的 bug。编程

使用 TS,在编辑器中就会明确的提醒咱们 a 的值可能为 null

clipboard.png

咱们并不必定要处理值 null 的状况,使用 const a = document.getElementById(‘id')! 能够明确告诉 TS ,它不会是 null,不过至少,这时候咱们清楚的知道本身想作什么。

使用 TS 的过程就是一种学习的过程

使用 TS 后,感受本身经过浏览器查文档的时间明显少了不少。不管是库仍是原生的 js 或者 nodejs,甚至是本身团队其它成员定义的类型。结合 VSCode ,会有很是智能的提醒,也能够很方便看到相应的接口的确切定义。使用的过程就是在加深理解的过程,确实「面向接口编程」自然和静态类型更为亲密。

好比说,咱们使用 Color 这个库,VSCode 会有下面这类提醒:

clipboard.png

不用去查文档,咱们就能看到其提供的 API。
若是咱们去看这个库的源文件会发现,能有提醒的缘由在于存在下面这样的定义:

// @types/color/index.d.TS
interface Color {
    toString(): string;
    toJSON(): Color;
    string(places?: number): string;
    percenTString(places?: number): string;
    array(): number[];
    object(): { alpha?: number } & { [key: string]: number };
    unitArray(): number[];
    unitObject(): { r: number, g: number, b: number, alpha?: number };
    ...
}

这种提醒无疑能加强开发的效率,虽然定义类型在早期会花费必定的时间,可是对于一个长期维护的比较大型的项目,使用 TS 很是值得。

一种学习 typescript 的路径

也许是由于,我以前从未系统的学习过一门静态语言,因此从开始学到感受本身基本入门了 TS 花的精力还挺多的。
学习 TS 的过程当中,主要参考了如下这些资料,你能够直接点击连接查看,也能够继续看后文,我对这些资料有着一些简单的分析。

在阅读上述资料的过程当中,我使用 TS 重写了一个基于 CRA 的简单可是很完整的前端项目,如今以为,使用 TS 来开发工做中的常见需求,应该都能应对了。若是你是刚刚开始学 TS,不妨参照下面的路径学习。

搭建 TS 运行环境

不要误解,并不是从零搭建。学习实践性很强的内容时,边学边练习能够帮咱们更快的掌握。若是你使用 React,借助 yarn 或者 create-react-app,可轻易的构造一个基于 TS 的项目。

在命令行中执行下述命令便可生产可直接使用的项目:

# 使用 yarn
$ yarn create react-app TS-react-playground --typescript
# 使用 npx
$ npx create-react-app TS-react-playground --typescript

随后若是须要,能够在tsconfig.json中添加额外的配置。

就我我的而言,我喜欢同步配置 TS-lint 与 prettier,已免去以后练习过程当中格式的烦恼。配置方法能够参考 Configure TypeScript, TSLint, and Prettier in VS Code for React Native Development 这篇文章,或者看个人配置记录

若是你不使用 React,TypeScript 官方文档首页就提供了 TS 配合其它框架的使用方法。

理解关键的概念

我一直以为,学习一项新的技能,清楚其边界很重要,相关的细节知识则能够在后续的使用过程当中逐步的了解。咱们都知道,TS 是 JS 的超集,因此学习 TS 的第一件事情就是要找到「超」的边界在哪里。

这个阶段,推荐阅读 TypeScript handbook — book,这本书其实也是官方推荐的入门手册。这里给的连接是中文翻译版的连接,翻译的质量很是好,虽然内容没有英文官方文档新,不过学习新的东西最好仍是从本身最熟悉的内容入手,因此不妨先看中文文档。阅读过程当中遇到的示例,均可以在上面搭建的 TS-playground 中练习一下,熟悉一下。

TS 作为 JS 的超集,其「超」其实主要在两方面

  • TS 为 JS 引入了一套类型系统;
  • TS 支持一些非 ECMAScript 正式标准的语法,好比装饰器;

关于第二点,TS 作的事情有些相似 babel,因此也有人说 TS 是 babel 最大的威胁。不过这些新语法,极可能你早就使用过,本文再也不赘述。

比较难理解的实际上是这套类型系统,这套类型系统有着本身的声明空间(Declaration Spaces),具备本身的一些关键字和语法。

对我来讲,学习 TS 最大的难点就在于这套类型系统中有着一些我以前不多了解的概念,在这里能够大体的梳理一下。

一些 TS 中的新概念

编程实际上就是对数据进行操做和加工的过程。类型系统能辅助咱们对数据进行更为准确的操做。TypeScript 的核心就在于其提供一套类型系统,让咱们对数据类型有所约束。约束有时候很简单,有时候很抽象。

TS 支持的类型以下:boolean,number,string,[],Tuple,enum,any,void,null,undefined,never,Object

TS 中更复杂的数据结构其实都是针对上述类型的组合,关于类型的基础知识,推荐先阅读基础类型一节,这里只讨论最初对我形成困扰的概念:

  • enum:

如今想一想 enum  枚举类型很是实用,不少其它的语言都内置了这一类型,合理的使用枚举,能让咱们的代码可读性更高,好比:

const enum MediaTypes {
  JSON = "application/json"
}

fetch("https://swapi.co/api/people/1/", {
  headers: {
      Accept: MediaTypes.JSON
  }
})
.then((res) => res.json())
  • never:

never 表明代码永远不会执行到这里,经常能够应用在 switch casedefault 中,防止咱们遗漏 case 未处理,好比:

enum ShirTSize {
  XS,
  S,
  M,
  L,
  XL
}

function assertNever(value: never): never {
  console.log(Error(`Unexpected value '${value}'`));
}

function prettyPrint(size: ShirTSize) {
  switch (size) {
      case ShirTSize.S: console.log("small");
      case ShirTSize.M: return "medium";
      case ShirTSize.L: return "large";
      case ShirTSize.XL: return "extra large";
        // case ShirTSize.XS: return "extra small";
      default: return assertNever(size);
  }
}

下面是上述代码在个人编辑器中的截图,编辑器会经过报错告知咱们还有未处理的状况。

clipboard.png

  • 类型断言:

类型断言其实就是你告诉编译器,某个值具有某种类型。有两种不一样的方式能够添加类型断言:

  • <string>someValue
  • someValue as string

关于类型断言,我看文档时的疑惑点在于,我想不到什么状况下会使用它。后来发现,当你知道有这么一个功能,在实际使用过程当中,就会发现能用得着,好比说迁移遗留项目时。

  • Generics(泛型):

泛型让咱们的数据结构更为抽象可复用,由于这种抽象,也让它有时候不是那么好理解。泛型的应用场景很是普遍,好比:

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

可以让某一种接口的子类型均可觉得 null。我记得我第一次看到泛型时也以为它很很差理解,不事后来多用了几回后,就以为还好了。

  • interface 和 type

interfacetype 均可以用来定义一些复杂的类型结构,最不少状况下是通用的,最初我一直没能理解它们两者之间区别在哪里,后来发现,两者的区别在于:

* `interface`建立了一种新的类型,而 type 仅仅是别名,是一种引用;
* `type` 不能被 `extends` 和 `implements`

在视频 Use Types vs. Interfaces from @volkeron on @eggheadio 中,经过实例对两者的区别有更细致的说明。

  • 类型保护

TS 编译器会分析咱们的程序并为某一个变量在指定的做用域来指明尽量确切的类型,类型保护就是一种辅助肯定类型的方法,下面的语句均可以用做类型保护:

* `typeof padding === "number"`
* `padder instanceof SpaceRepeatingPadder`

一个应用实例是结合 redux 中的 reducer 中依据不一样的 type,TS 能分别出不一样做用域内 action 应有的类型。

  • 类型映射

类型映射是 TypeScript 提供的从旧类型中建立新类型的一种方式。它们很是实用。好比说,咱们想要快速让某个接口中的全部属性变为可选的,能够按照下面这样写:

interface Person {
    name: string;
    age: number;
}
type PartialPerson = { [P in keyof Person]?: Person[P] }

还有一个概念叫作 映射类型,TS 内置一些映射类型(其实是一些语法糖),让咱们能够方便的进行类型映射。好比经过内置的映射类型 Partial ,上面的表达式能够按照下面这样写:

interface Person {
    name: string;
    age: number;
}
type PartialPerson = Partial<Person>

常见的映射类型,能够参看这篇文章 — TS 一些工具泛型的使用及其实现,除了作为语法糖内置在 TS 中的映射类型(如Readonly),这篇文章中也提到了一些未内置最 TS 中可是很实用的映射类型(好比 Omit)。

  • 第三方的库,如何获得类型支持

咱们很难保证,第三方的库都原生支持 TS 类型,在你使用过一段时间 TS 后,你确定安装过相似 @types/xxx 的类型库,安装相似这样的库,实际上就安装了某个库的描述文件,对于这些第三方库的类型的定义,都存储在DefinitelyTyped 这个仓库中,经常使用的第三方库在这里面都有定义了。在 TypeSearch 中能够搜索第三方库的类型定义包。

关于类型,还有一些不少其它的知识点,不过一些没有那么经常使用,一些没有那么难理解,在此暂不赘述。

消化学到的新概念

我首次看完《TypeScript handbook》时,确实以为本身懂了很多,可是发现动手写代码,仍是会常常卡住。追其缘由,可能在于一会儿接收了太多的新概念,一些概念并无来得及消化,这时候我推荐看下面这门网课:

看视频算是一种比较轻松的学习方式,这门课时长大概是一个小时。会把 TypeScript handbook 这本书中的一些比较重要的概念,配合实例讲解一次。能够跟着教程把示例敲一次,在 vscode 中多看看给出的提示,看完以后,对 TS 的一些核心概念,确定会有更深的理解。

模仿和实践

想要真的掌握 TS,少不了实践。模仿也是一种好的实践方式,已 React + TypeScript 为例,比较推荐的模仿内容以下:

  1. TypeScript-React-Starter ,这是微软为 TS 初学者提供的一个很是好的资料,能够继续使用咱们上面构建的 playground ,参照这个仓库的 readme 写一次,差很少就能知道 TS 结合 React 的基本用法了;
  2. GitHub - react-typescript-cheaTSheet,这个教程也比较简单,不过上面那个教程更近了一步,依据其 readme 继续改造咱们的 playground 后,咱们能知道,React + Redux + TypeScript 该如何配合使用;
  3. react-redux-typescript-guide ,这个教程则展现了基于 TypeScript 如何应用一些更复杂的模式,咱们也能够模仿其提供的用法,将其应用到咱们本身的项目中;
  4. Ultimate React Component Patterns with Typescript 2.8 ,这篇文章则能够作为上述内容的补充,其在掘金上有汉语翻译,点赞量很是高,看完以后,差很少就能了解到若是使用 TS 应对各类 React 组件模式了。
  5. Use TypeScript to develop React Applications — egghead.io,随后若是想再轻松一点,则能够再看看这个网课,跟着别人的讲解,回头看看本身模仿着写的一些代码,也许会有不一样的感触;

至此,你确定就已经具有了基础的 TS 开发能力,能够独立的结合 TS 开发相对复杂的应用了。

更深的理解

固然也许你并不会知足于会用 TS,你还想知道 TS 的工做原理是什么。这时候推荐阅读下面两篇内容:

关于 TS 的原理,我尚未来得及仔细去看。不过 AST 在前端中的应用还真是多,待我补充更多的相关知识后,也许会对 AST 有一个更全面的总结。

TS 固然也不是没有缺点,The TypeScript Tax 是一篇很是优秀的文章,阅读这篇文章能让咱们更为客观看待 TS,虽然站在做者的角度看,TS 弊大于利,主要缘由是 TS 提供的功能大多均可以用其它工具配合在必定程度上代替,并且类型系统会须要写太多额外的代码,类型系统在必定程度上也破坏了动态语言的灵活性,让一些动态语言特有的模式很难在其中被应用。做者最终的结论带有很强的主观色彩,我并非很是承认,可是这篇文章的分析过程很是精彩,就 TS 的各类特性和如今的 JS 生态进行了对比,能让咱们对 TS 有一个更全面的了解,很是推荐阅读,也许你会和我同样,看完这个分析过程,会对 TS 更感兴趣。

TS 每隔几个月就会发布一个新的小版本,每一个小版本在 TypeScript 官方博客 上都会有专门的说明,可用用做跟进学习 TS 的参考。

参考

下述参考内容在文中,都有连接,若是都看过,则无需再重复查看了。

你也能够来知乎专栏和我讨论

相关文章
相关标签/搜索