type
Post
status
Published
date
Apr 1, 2019
slug
summary
请先思考一个问题:你见过没有类型的编程语言吗?答案当然是没有,类型是一门编程语言的基础
tags
JavaScript
TypeScript
category
技术分享
icon
password

1.类型系统

请先思考一个问题:你见过没有类型的编程语言吗?答案当然是没有,类型是一门编程语言的基础。这是由于在计算机中,任何数值都是以一组比特简单组成的,但不同类型需要不同的硬件单元参与计算,没有类型,硬件无法区分存储器地址、脚本、字符、整数、以及浮点数。所以任何一门编程语言都有类型系统,只是有强弱之分、静态动态之分。Javascript 就是一门弱类型的、动态检查的语言。

2.为什么需要引入静态类型系统

  • 提高代码的可读性
  • 方便对代码分析
  • 保证重构的可靠性
  • 通常 IDE 能更好地支持
  • 编码阶段可以捕获类型相关错误
可以看出,引入静态类型系统主要是方便代码的维护。

3.Flow vs TypeScript

众所周知,flow 是 React 源码中所使用的静态类型检查器,但是可能并没有自己用过,由于大家对 Typescript 更加熟悉,我通过与它对比,来为大家浅析下 flow 的特点。

3.1 区别

TyepScript:使用方便,工具支持健全。
  • 微软出品,C# 之父 Anders Hejlsberg 参与设计
  • 基于 ES6 语法(也包含是 ES7 / ES8 )
  • 类型是可选的
  • 可以编译出纯净、 简洁的 JavaScript 代码
Flow:健全,没有把运行时异常作为目标。
  • Facebook 出品
  • 是一种静态类型检查器,旨在快速查找 JavaScript 应用程序中的错误
  • 不是编译器,而是检查器
  • 可以很容易地通过 babel 在运行时删除类型注释

3.2 实例

3.2.1 基本

TypeScript:
let obj: string; obj = 'yo'; // Error: Type 'number' is not assignable to type 'string'. obj = 10;
// types can be inferred (return type) function sayIt(what: string) { return `Saying: ${what}`; } const said: string = sayIt(obj);
class Sayer { // mandatory what: string; constructor(what: string) { this.what = what; } // return type if you want to sayIt(): string { return `Saying: ${this.what}`; } }
Flow:
let obj: string; obj = 'yo'; // Error: number: This type is incompatible with string obj = 10;
function sayIt(what: string) { return `Saying: ${what}`; } const said: string = sayIt(obj);
class Sayer { what: string; constructor(what: string) { this.what = what; } sayIt(): string { return `Saying: ${this.what}`; } }
没错,基本功能上来看,两者完全一样

3.2.2 非空类型(Non-Nullable Types)

TypeScript:
function foo(num: number) { if (num > 10) { return 'cool'; } }
// cool const result: string = foo(100); console.log(result.toString());
// still cool? console.log(foo(1).toString());
// error at runtime "Cannot read property 'toString' of undefined";
TypeScript 不 catch 这个
Flow:
function foo(num: number) { if (num > 10) { return 'cool'; } } // error: call of method `toString`. // Method cannot be called on possibly null value console.log(foo(100).toString());
Flow 可以 catch 这个,为什么呢? 因为 FLow 不会将字符串推断为返回类型
// error: return undefined. This type is incompatible with string function foo(num: number): string { if (num > 10) { return 'cool'; } }
// nullable type: the one inferred function foo(num: number): string | void { if (num > 10) { return 'cool'; } }
// to fix this, we need to check the result const fooed: string | void = foo(100); if (fooed) { fooed.toString(); }
但是 TypeScript 2.x 也可以 catch 这个了,通过开启strictNullChecks 总结一下:
  • 在 TypeScript 1 中类型默认是可空的
  • 在 TypeScript 1.x 中不为空的限制是可能的了
  • 在 Flow 中类型默认是不可空的。
  • 在 TysScript 2.x 中在“TSCONFIG.JSON”中使用strictNullChecks默认类型不可空。

3.2.3 注意

class Person { name: string; constructor() { // why no error? } } const olli: Person = new Person();
// issues error as expected const daniel: Person = {};
Flow 和 TypeScript 2.0 都不能 catch 这个,但是 TypeScript 中这个不会被修复了。

3.3 泛型

class Animal { name: string; constructor(name: string) { this.name = name; } }
class Dog extends Animal { // just to make this different from cat goodBoyFactor: number; }
class Cat extends Animal { purrFactor: number; }
泛型:类型可以由其他参数化
let cats: Array<Cat> = []; // can only contain cats let animals: Array<Animal> = []; // can only contain animals // nope, no cat cats.push(10); // nope, no cat // cool, is a cat cats.push(new Cat('Purry')); // cool, cat is a sub type of animal animals.push(new Cat('Purry'));
到目前为止,Flow 和 TypeScript 的工作方式是一样的。但是…
TypeScript:
let cats: Array<Cat> = []; // can only contain cats let animals: Array<Animal> = []; // can only contain animals // error TS2322: Type 'Animal[]' is not assignable to type 'Cat[]'. // Type 'Animal' is not assignable to type 'Cat'. // Property 'purrFactor' is missing in type 'Animal'. cats = animals; // wow, works, but is no longer safe animals = cats; // ouch: cats.forEach(cat => console.log(`Cat: ${cat.name}`)); // Cat: Purry // Cat: Brutus // Cat: Twinky
TypeScript 允许鸟和狗成为猫 :)
Flow:
let cats: Array<Cat> = []; // can only contain cats let animals: Array<Animal> = []; // can only contain animals // ERROR// property `purrFactor` of Cat. Property not found in Animal cats = animals; // same ERROR animals = cats;
但 Flow 不会 这是为什么呢?
  • TypeScript
    • 如果要分配的类型具有更特殊的类型参数,则参数类型是兼容的
    • 似乎最直观
    • 允许明显错误的代码
  • Flow
    • 使用泛型类型,可以从不变量(精确匹配)、协变(更特殊)和逆变(更常见)中进行选择。
    • Flow 中的数组具有不变的参数类型
    • 更有表现力
    • 更难理解

4.时间线

  • 2012-10 ts 诞生
  • 2013-09 ts 0.9 正式版发布
  • 2014-09 ts 1.0
  • 2014-11 flow 诞生
  • 2016-07 React v15.2.0 引入 flow
  • 2016-08 ts 2.0,引入-strictNullChecks,支持严格空检查
  • 2016-11 ts 2.1,支持对象扩展运算符
  • 2017-04 ts 2.3,支持迭代器
  • 2019-06 ts 3.5,支持Omit 辅助 type
  • 2019-10 ts 3.7,支持 Optional Chaining && Nullish Coalescing (可选链式调用“?.”与空值合并运算符“??”)

5.结语

flow 的出现一定程度推动了 js 静态类型检查的流行,让 Javascript 开发者拥抱静态类型系统,也让我们尝到了静态类型的甜头,但最终发现 Typescript 才是真香,其实可以说 flow 是促进了 typescript 的发展。
Node CLI 工具开发和原理WebGL 入门