返回博客归档

ISSUE / 007

TypeScript 预研

TypeScript 预研 TypeScript 是什么 释义 官网对 TypeScript 的释义: TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any hos…

树影落在墙面上的编辑照片

TypeScript 预研

TypeScript 是什么

释义

官网对 TypeScript 的释义:

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
Any browser. Any host. Any OS. Open source.

翻译过来就是:

TypeScript 是 JavaScript 类型的超集,它可以编译成纯 JavaScript。
TypeScript 可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。

主要理解第一句话:TypeScript 是 JavaScript 类型的超集,它可以编译成纯 JavaScript

  • 类型表明了 TypeScript 是带有类型系统的,但并不等同于强类型语言。

  • 超集意味着你只要把你 .js 的文件重名为.ts就可以被 TypeScript 正确编译,它支持所有 JavaScript 的语法并进行了一些语法上的扩展。如果你本身已经熟悉 JavaScript 则可以很容易上手 TypeScript,并不会像 CoffeeScript、Dart 稍微难上手。

  • 编译成纯 JavaScript则意味着 TypeScript 需要一个编译器(tsc),并不能被直接在 Browser、nodejs 等环境下直接运行

同 Lint 工具 和 Flow 的区别

同 Lint 工具的区别

  • Lint 工具强调的是代码规范检测
  • Lint 无法做到静态类型分析

同 Flow 的区别

  • 都是 checker,都要借助 compiler
  • 类型系统功能都差不多(实现不同,typescript 是 ast 导向,flow 是 图形导向),几乎能做到同样的事。但 TypeScript 有更好的文档,更好的社区支持
  • Flow 是在原 js 文件顶部上添加 // @flow 注释,而 Typescript 是将 .js 重命名为 .ts 文件
  • 在类型覆盖上 TypeScript 更为平滑, Flow 类型覆盖的速度更快一些 image

TypeScript 趋势报告

  • 在 GitHub 的 2018年趋势报告中,TypeScript 的受欢迎程度得到大幅提升,从去年的第十已经上升到第七位了 image

  • 同样在上升最快的语言排行榜中 TypeScript 也排在了第三位 image

  • 再放一张 npm 下载趋势图 image

  • 谷歌中 TypeScript 的 Trends 图也可以看出 TypeScript 的热度也一直呈上升状态 image

  • 更意外的是区域搜索热度,中国排在了第一位,可以看出已经越来越多的中国开发者在了解 TypeScript 了 image

  • 再看下都有哪些开源项目在使用 TypeScript

    • Angular 2 开始就已经使用 TypeScript 构建应用
    • React 最热门 UI 组件库的 ant-design 使用 TypeScript 开发
    • 前端最热门的编辑器 VSCode 是使用 TypeScript 开发的
    • GitHub 的桌面端程序是使用 TypeScript 开发的
    • Node.js 的原始作者(Ryan Dahl)的新项目 deno 正使用 TypeScript 开发,试图解决 Node.js 面临的一些问题。
    • DefinitelyTyped 中已经收录了 5481 个类型声明文件,绝大多数的第三方库都会添加类型声明文件,而一个有类型声明文件的第三方库除了方便开发者使用也同样更值得开发者信赖

TypeScript 能做什么

静态类型系统

In programming languages, a type system is a set of rules that assigns a property called type) to the various constructs of a computer program, such as variables), expressions), functions) or modules.[1] These types formalize and enforce the (otherwise implicit) categories the programmer uses for data structures and components (ex: "string", "array of float", "function returning boolean"). The main purpose of a type system is to reduce possibilities for bugs) in computer programs[2] by defining interfaces between different parts of a computer program, and then checking that the parts have been connected in a consistent way. 【维基百科】

在编程语言中,类型系统是一个规则集合,给程序中的变量、表达式、函数、模块等程序构建元素分配叫做类型的属性。这些类型明确并强制(也可能是含蓄的)程序员如何使用数据结构。类型系统的主要目的是通过定义程序不同部分间协作的接口,并检查不同部分以始终如一的方式协作,来减少程序可能产生的bug。这种检查可能是静态的(编译期)或动态的(运行时),或者既有静态的也有动态的。

那为什么需要使用类型系统呢?咱们举个不太严谨的比喻。

说你在网络上跟一个自称是小姐姐的妹子聊了得有大半年,这大半年通过你们两的聊天,你脑海里已经给这位“小姐姐”贴上了这样的标签:颜值高、身材好、性格还温柔体贴、理想的结婚对象。时机也成熟了,你们准备线下见个面了,相约人民广场,暗号手里拿朵玫瑰。时间也到了,你准时来到人民广场,诶,目标出现了,你在远处先观察了会,发现不对啊,这又矮又胖脸蛋也不好看,发消息问:

“你是穿着红色羽绒服站在喷泉边上的那位吗?”

“你是穿着黑色夹克骑着小黄车的那个?”

“不是”

“不是”

“刚刚朋友打电话说有急事,今天就先不见面了”

“好的,我也有点事”。

这就是没有类型系统的坏处,我原本想找个奶茶妹妹,怎么给我安排了个玉凤姐姐啊。这时候要是有“类型系统”,画面应该是这样的:

“咱们聊天前,身份证照片先交换来看看吧!”

“好的”

“你是白羊座的啊!我觉得咱们不适合”

“我也不喜欢水瓶座的男生,白白”

检测错误

静态类型检测可以在开发中就检测出一些低级错误而不是在上线之后才发现。

Talk is cheap. Show me the code. 屁话少说,放码过来。

  • 类型错误 ```js // javascript function add(x, y) { return x + y }

add('1', 1) // '11'

ts function add(x:number, y:number):number {

return x + y

}

add('1', 1) // error: Argument of type '"1"' is not assignable to parameter of type 'number'.


- 非空判断

js let response;

// 当response返回过来的是null或者undefined时 const list = response.map(item => item * item); // Uncaught TypeError: Cannot read property 'map' of undefined


ts let response: number[] | null | undefined;

const list = response.map(item => item * item); // response 可能是null或者undefined


#### 类型推断

ts function fullName(firstName: string, secondName: string) {

return firstName + secondName

}

const name = fullName('hello' + 'world');

name.map(item => item * item) // Property 'map' does not exist on type 'string'.


ts class Cat { miao() {} }

class Dog { wang() {} } const cat = new Cat(); cat.wang();

// ts output // Property 'wang' does not exist on type 'Cat'.


---
### 更多的基础类型
TypeScript 有 `布尔值`、`数字`、`字符串`、`数组`、`元组`、`枚举`、`Any`、`Void`、`Null 和 Undefined`、`Never`、`Object` 

更多的基础数据类型意味我们在声明变量的时候有更多的选择,如我们可以使用`枚举`来消除我们代码中的 `Magic Number(魔术数字)`:

ts enum BOOL = {

TRUE: '1',
FALSE: '0'

}

if (status === BOOL.TRUE) {

// xxx

}


虽然在 js 中我们也可以使用 `Object` 来模拟枚举,但是 TypeScript 原生支持会对新人更友好,可以更好的规范代码。

---

### class 的增强
从 ES6 开始,我们就已经能够使用基于类的面向对象的方式。并且 typescript 在此基础上进行了补充。


#### 公共,私有与受保护的修饰符-访问权限的控制

##### 默认为 `public`

ts class Animal {

public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
}

}


##### private 修饰符
当成员被标记成 private时,它就不能在声明它的类的外部访问。比如:

ts class Animal {

private name: string;
constructor(theName: string) { this.name = theName; }

}

new Animal("Cat").name; // 错误: 'name' 是私有的.


##### protected 修饰符
protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。例如:

ts class Person {

protected name: string;
constructor(name: string) { this.name = name; }

}

class Employee extends Person {

private department: string;

constructor(name: string, department: string) {
    super(name)
    this.department = department;
}

public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}

}

let howard = new Employee("Howard", "Sales"); console.log(howard.getElevatorPitch()); console.log(howard.name); // 错误


#### 基于接口实现类
下面这个例子体现了一个系统模块可以抽象的看做一个 TypeScript 定义的接口。使用接口来描述设计,使设计脱离实现,实现面向接口编程:

ts interface ClockInterface {

currentTime: Date;
setTime(d: Date);

}

class Clock implements ClockInterface {

currentTime: Date;
setTime(d: Date) {
    this.currentTime = d;
}
constructor(h: number, m: number) { }

}


### 模块系统增强
在 ES6 模块系统的基础上,还实现了命名空间:

ts namespace N { export namespace NN {

export function a() {
  console.log('N.a');
}

} }

N.NN.a();


## 为什么要使用 TypeScript

### 提高代码的稳健度
在 coding 过程中消灭低级错误,而不是线上。特别是在没有单元测试覆盖的情况下,TypeScript 的静态类型检测能帮我们避免大部分的低级错误。

### 提高代码的维护性
长期迭代维护的项目永远不可能只有一个人,小型项目同样也可能有多个人一起协同开发。如果使用 JavaScript 开发,在没有 code review 的情况下,成员有没有写适当的注释或者 README 全靠自觉了,而通过 code review 来检查文档的完备性成本又太大,可是不做又可能出现`文档丢失`的情况。

而如果使用 TypeScript 开发,类型即文档,再加上接口的支持。维护者可以通过类型和接口的声明就可以明白开发者的意图。

外加 typedoc 这类文档工具的支持,可以生成独立的文档文件也是方便项目知识体系的建立。

### 在 IDE 的支持下提高开发效率
目前主流的前端 IDE 都对 TypeScript 做了大力支持。在我们使用有类型声明文件的第三方库的时候,IDE 强大的智能提示可以告诉使用具体方法的签名等,如果我们使用 TypeScript 开发项目,配合 IDE 的智能纠错可以实时代码是否存在 bug 。

### 利于重构
好的代码不是写出来的,而是重构出来的。重构是我们构建优秀代码必不可缺的一个重要环节。

即使是做最简单的 `rename` 操作,在使用 JavaScript 开发的情况下都得小心翼翼,更别提项目中可能还有全局变量这种存在的情况了,你可能得 `ctrl + F` 去查找所有使用到这个`name`的情况,并且这还不是百分百安全的。如果你使用 TypeScript ,编译器会帮你检测使用的地方,你可以放心大胆做 `rename` 操作。

再说一种情况,如果原先约定好的后端接口现在要新增一个入参了,在 JavaScript 中,这让人很头疼,你又得 `ctrl + F` 去查找所有的使用情况,同样不可能做到百分百安全。而我们只要修改 TypeScript 的接口定义,编译器会自动帮我们去检测所有的实现,同样安全。

### 更低的测试成本
拿上面那个修改接口的例子来说,即使我们为 JavaScript 添加了测试,而当碰到修改类似入参的情况,我们又得重新书写我们的测试用例,这成本就相当大了,而如果你不去对应的修改测试用例,那测试就会毫无意义,咱们看个例子,看看 TypeScript 相比 JavaScript 的测试成本,先看看 JavaScript的:

js function greeter(person) {

if (!person || !person.firstName || !person.lastName) {
    throw new Error('invalid arguments');
}

return "Hello, " + person.firstName + " " + person.lastName; }


// Jasmine spec: describe("greeter", function() {

it("returns greeting on valid input", function() {
    expect(
        greeter({firstName: 'James', lastName: 'Hetfield'})
    ).toEqual('Hello, James Hetfield');
});

it("throws on invalid input", function() {

    expect(() => greeter()).toThrowError('invalid arguments');
    expect(() => greeter(5)).toThrowError('invalid arguments');
    expect(() => greeter({firstName: 'Jason'})).toThrowError('invalid arguments');
});

});


而 TypeScript 则会更简洁点:

ts interface Person {

firstName: string;
lastName: string;

}

function greeter(person: Person) {

return "Hello, " + person.firstName + " " + person.lastName;

}

// Jasmine spec: describe("greeter", function() {

it("returns greeting", function() {
    expect(
        greeter({firstName: 'James', lastName: 'Hetfield'})
    ).toEqual('Hello, James Hetfield');
});

});


**所以,如果你还未开始接触 TypeScript,那么请把 TypeScript 加到你的 2019 技能清单里。**

## 使用 TypeScript 的成本
一个工具的使用成本是考量最后是否使用它的最终标准。

### 学习成本
TypeScript 作为 JavaScript 的超集,在语法上是完全兼容 JavaScript 的,只不过又拓展了一些语法糖。而得力于优秀官方文档,又进一步降低我们的学习成本。

### 最佳实践的成本 
学会用和能用好它之间还很长的路要走。虽然说在 TypeScript 你可以指定任何类型为`any` 或者为部分代码添加类型注解来快速开始使用 TypeScript,这其实已经比完全没有写类型的 JavaScript 好多了,但如果类型覆盖没有做到很高(理想是百分百)的话,我们就不能充分发挥 TypeScript 给我们带来的好处。

特别注意的是,在使用 TypeScript 的初期,经常会碰到一些特殊的类型怎么写,这需要一定的经验,所以需要一定的时间来让团队去摸索最佳实践,毕竟我们不希望看到为了写类型而写类型的情况,这实际上会拖慢你的开发效率。

### 什么时候选择 TypeScript 开发
- 在面对生命周期不长的中小型项目时,选用 TypeScript 开发是非必须的,但同样推荐使用 TypeScript 进行开发
- 面对生命周期长的大型项目,强烈推荐使用 TypeScript 开发,相信社区的大项目的选择是不会错的
- 开发第三方库或者框架或者公共模块等强烈推荐使用 TypeScript 进行开发。