浅析TypeScript

TypeScript 实践

title

TypeScript 是什么

  1. 是微软开发的一项开源技术;
  2. 属于 JavaScript 类型的超集,即 JavaScript 支持的 TypeScript 全都支持,但是 TypeScript 支持的 JavaScript 原生不一定支持;
  3. 给予原生 JavaScript 类型支持,原生 JavaScript 其实是动态类型的编程语言,有一句话大家应该听说过,「动态类型一时爽,代码重构火葬场」;而 TypeScript 的存在,就相当于对 JavaScript 在大型项目的开发上,给予有力的支撑。

为什么选择 TypeScript

辅助开发流

我们在进行软件开发的时候,通常都是接口先行的(这里的接口不是狭义上的 interface )。
具体到前端来说,面对一个页面,即是自顶向下的,设计组件。
这其中就包括组件功能的划分,组件之间的交互,以及理解如何组合组件形成一个新的子系统,各个新的子系统又联系在一起,形成一个更大的系统,最终有效的结合在一起,组成最后的页面。
在这些逻辑关系中,接口都发挥着重要的作用。而现如今的前端,除了需要处理 view 层逻辑,还需要处理 service 逻辑以及页面中间态逻辑,这其中 TypeScript 能发挥巨大价值。
没有理清楚接口就尝试写代码,相当于一开始就陷入细节的陷阱里。
其实不仅是写代码,如果有熟悉写书的朋友一定知道,在写一本书之前,最先做的就是写目录,一本书的目录写好了,那么这本书的整体方向脉络基本就决定,而剩下要去做的,就是填充内容了。

更早发现错误

前端业务里,有大部分的 bug 是由于调用方对实现方所需的类型的不确定导致的

img

rollbar 是一个前端监控平台,通过其给出的 数据 可以看出,大部分问题基本都是类型问题,而通过 TypeScript 的类型校验,可以直接在编译阶段直接规避该问题。

显著提升的代码效率

当你的项目中使用 TypeScript 时,大部分时候我们都不需要关注所调用的代码具体是怎么实现的,依赖了哪些参数,只要通过类型就可以初步判断。
并且在 vscode 编辑器强大的支持下,我们可以实现诸如代码自动引入,类型未编译即可校验等强大功能。

Mar-06-2019 22-47-28.gif

快速入门

JavaScript vs TypeScript

通过 JavaScript 与 TypeScript 的对比,相信大家能快速理解并学习 TypeScript 。

变量声明

// JavaScript
let name = 'Mike';
let age = 18;
name = 10; // 正常运行
// TypeScript
let name:string = 'Mike';
let age:number = 18;
name = 10; // 由于 name 的类型已经定义为 string ,因此会抛出异常
// 通常变量类型不需要显示的声明,TypeScript 会自己解析变量类型
let height = 20; // 此时 height 为 number 类型

函数声明

// JavaScript
function func(params1: number, params2: string) {
  // ...
}
func(1, '1'); // 编译通过
func(1, 1); // 编译通过
// TypeScript
function func(params1: number, params2: string) {
  // ...
}
func(1, '1'); // 编译通过
func(1, 1); // 编译失败

React 组件

// .jsx file
import React from 'react';
import T from 'prop-types';

class App extends React.Components {
  static propTypes = {
    name: T.string.isRequired,
  };
  state = {
    age: 13,
  };
  render() {
    return (
      <div>
        {this.props.name} age:{this.state.age}
      </div>
    );
  }
}
// .tsx file
import React from 'react';
interface AppProps {
  name: string;
}
interface AppState {
  age: number;
}
class App extends React.Components<AppProps, AppState> {
  state = {
    age: 13,
  };
  render() {
    return (
      <div>
        {this.props.name} age:{this.state.age}
      </div>
    );
  }
}

理解重载

重载是 Java ,C++ 等语言具有的一种特性。在写代码的过程中,我们常常会有一个函数,需要根据不同的参数,进行不同的逻辑处理。而重载可以定义参数不同的函数,根据变量名的不同,自动执行对应变量名的函数。
下面以 Java 为例

public class Overloading {
    public int test(){
        System.out.println("test1");
        return 1;
    }

    public void test(int a){
        System.out.println("test2");
    }

    //以下两个参数类型顺序不同
    public String test(int a,String s){
        System.out.println("test3");
        return "returntest3";
    }

    public String test(String s,int a){
        System.out.println("test4");
        return "returntest4";
    }

    public static void main(String[] args){
        Overloading o = new Overloading();
        System.out.println(o.test());
        o.test(1);
        System.out.println(o.test(1,"test3"));
        System.out.println(o.test("test4",1));
    }
}

然而 TypeScript 里的重载并没有那么智能,其重载的主要目的还是做静态类型检查。

function getItself(it: string): string;
function getItself(it: number): number;

function getItself(it: string | number) {
  let result;
  if (typeof it === 'string') {
    result = '123';
    return result;
  } else {
    result = 123;
    return result;
  }
}

const a = getItself(123); // a 此时为 number 类型
const b = getItself('123'); // b 此时为 string 类型

理解 interface

在 TypeScript 文档里,花了很大的篇幅去描述 interface ,即接口。可以说明 interface 是 TypeScript 里非常重要的概念。

One of TypeScript’s core principles is that type-checking focuses on the shape that values have. This is sometimes called “duck typing” or “structural subtyping”. In TypeScript, interfaces fill the role of naming these types, and are a powerful way of defining contracts within your code as well as contracts with code outside of your project.

duck typing ,也就是鸭子类型,摘自维基百科里说,「When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.」
其实在我们平时写代码的过程中,广泛的使用了鸭子类型,例如

const coordinates = { x: 1, y: 3 };
function printCoordinates(params) {
  console.log(`x:${params.x};y:${params.y}`);
}
printCoordinates(coordinates);

如上对于 coordinates 对象的使用便是鸭子类型,即我们不关心这个变量是从哪个类继承来的,只要它有 x 坐标,y 坐标即可。
正是由于我们代码中广泛的使用,且其非常容易发生错误( rollbar 报错排行榜第一名),因此 TypeScript 引入 interface 帮助我们加以辅助。

const coordinates = { x: 1, y: 3 };
function addCoordinates(params) {
  return params.x + params.y + params.z;
}
addCoordinates(coordinates);

由于调用方并不知道 addCoordinates 参数对象,还需要一个 z 值,便会导致代码直接报错,页面 crash。

const coordinates = { x: 1, y: 3 };
interface ParamsInterface {
  x: number;
  y: number;
  z: number;
}
function addCoordinates(params: ParamsInterface) {
  return params.x + params.y + params.z;
}
addCoordinates(coordinates); // 编译不通过

可以看到使用了 TypeScript 加持后,这类问题可以直接在编译时,甚至加上编辑器的支持,可以直接在写代码的过程中,就能发现错误。

理解泛型

假设我们要实现一个方法,其作用就是将对象包装成一个数组,用 JavaScript 实现即时:

function toArray(params) {
  return [params];
}
toArray({ name: 'Mike' });

然而当使用 TypeScript 实现的时候,我们需要在执行前就定义好函数返回的类型,但是我们又不能确定这个对象到底是什么类型,这里就可以借助泛型来实现:

function toArray<T>(params: T): T[] {
  return [params];
}
toArray<{ name: string }>({ name: 'Mike' });

其实可以简单地将泛型理解为类型的变量,在这里,通过给 toArray 提供一个泛型变量,让 toArray 可以根据不同类型,返回不同的类型。

高级技巧

索引访问操作符T[K]

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
  return o[name]; // o[name] is of type T[K]
}
getProperty({ age: 18 }, 'age');

如上 T[K] 返回的类型就是 number
首先泛型 T 代表传入参数,即 { age: number } ,第二个 name 被约束成 keyof T ,也就是 T 对象的 key ,在这里就是 age 。那么返回值 T[K] 就能很直白的推断出为 number 类型。

泛型约束

通过对泛型进行 extends ,对类型进行一个约束。
例如希望类型都具有 length 类型

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length); // Now we know it has a .length property, so no more error
  return arg;
}

映射类型 [P in Keys]

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };

等价于

type Flags = {
  option1: boolean;
  option2: boolean;
};

将所有属性变成可选属性

type Option<T> = { [P in keyof T]?: T[P] };
interface Person {
  name: string;
  age: number;
}
type OptionPerson = Option<Person>;

接受两个类型,去除 T 中的 U

Exclude<T,U>

Exclude<'age'|'name','age'> // 'age'

同样接受两个类型,提取 T 中的 U

Extract<T,U>

Extract<'age'|'name'|'height','age'|'weight'> // 'age'

更多可能性

现在开发流程,前后端都各自维护一个系统,彼此独立且随着业务进展越来越大。而前后端之间唯一的桥梁就是接口文档(例如 proto ),这个桥梁看似可行,其实是非常脆弱的。
为什么这么说,因为其中一端产生变化,另外一端其实是无感的。 往往只能通过人为的方式去告知另一端发生的变化,(而这里便是沟通成本),并且由于经常有时候,一个后端服务被 N 个应用调用,因此其需要通知到 N 个相关维护人员,类似广播事件。而对于被告知的一方,其也需要人肉的方式去检查代码由于接口变更所引起的变化。
对于需要人肉检查代码引起的变化,可以通过 TypeScript 对接口 modal 进行类型规范,很大程度上加以改善;但是对于变更维护来说,仍然有许多人工的,重复性的劳动在里面。 这是大家需要解决的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容

  • 基于2018年Stack Overflow Developer的调研,TypeScript作为编程语言比JavaS...
    Ming_Hu阅读 2,186评论 1 33
  • 概述 TypeScript本质上是向JavaScript语言添加了可选的静态类型和基于类的面向对象编程,同时也支持...
    oWSQo阅读 8,467评论 1 45
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,034评论 1 32
  • "Unterminated string literal.": "未终止的字符串文本。", "Identifier...
    两个心阅读 7,813评论 0 4
  • 庸碌不会让你变得更有价值
    你看这是谁阅读 77评论 0 0