TypeScript 是基于 JavaScript 的编程语言,它解决了 JavaScript 自有类型系统的不足,通过使用 TypeScript 可以大大提高代码的可靠程度。
今天我们要从介绍编程语言的类型开始,再介绍 JavaScript 中类型体统存在的问题,再到为解决 JavaScript 类型系统的不足诞生出的两种最主流的 JavaScript 类型系统方案:Flow 和 TypeScript。
你准备好了吗?
大纲如下:
- 不同类型的区别与意义:强类型与弱类型、静态类型与动态类型
- JavaScript 类型系统的问题,给我们的开发工作都造成了哪些影响?
- Flow 介绍 - JavaScript 的类型检查器
- Flow 快速上手(未完成)
- Flow 编译移除注解和开发工具插件(未完成)
- Flow 类型推断(未完成)
- Flow 类型注解(未完成)
- Flow 原始类型、数组类型、对象类型、函数类型、特殊类型、Mixed与Any、类型总结(未完成)
- Flow 运行环境 API(未完成)
- TypeScript 概述(未完成)
- TypeScript 快速上手(未完成)
- TypeScript 配置文件(未完成)
- TypeScript 原始类型(未完成)
- TypeScript 标准库声明(未完成)
- TypeScript 作用域问题(未完成)
- TypeScript Object 类型、数组类型、元组类型、枚举类型、函数类型、任意类型(未完成)
- TypeScript 隐式类型推断和类型断言(未完成)
- TypeScript 接口(未完成)
- TypeScript 类的基本使用(未完成)
- TypeScript 类与接口(未完成)
- TypeScript 抽象类(未完成)
- TypeScript 泛型(未完成)
- TypeScript 类型声明(未完成)
Flow 是一个小工具,它能够弥补 JavaScript 的类型系统的不足,而 TypeScript 则是基于 JavaScript 基础之上的编程语言,TypeScript 要学习许多新知识,但还好 TypeScript 是渐进式的,我们可以先用起来慢慢适应。
1. 不同类型的区别与意义:强类型与弱类型、静态类型与动态类型
在具体介绍 JavaScript 自有类型系统存在的问题之前,我们先来解释两组在区分不同编程语言时经常提及的名词:强类型与弱类型、静态类型与动态类型。
为什么会通过类型去区分不同编程语言呢?
实际上,我们在区分编程语言时是分别在类型安全和类型检查两个角度考量的。
- 类型安全 - 强类型与弱类型
从类型安全的角度说,编程语言就划分为了两种:强类型与弱类型。强类型规定了在语言层面就要限制函数的实参类型必须与形参类型相同,弱类型在语言层面则不会限制实参的类型。 由于强弱类型的定义区分根本不是某一个权威机构的定义,而是几个专家所陈述出的概念,这就导致了后人对强弱类型界定的细节上出现了不一样的理解,但大家都相同描述了强类型具有更强的类型约束,而弱类型中则几乎没有什么约束,具体的区别是这样的:**强类型语言中不允许任意的隐式类型转换,弱类型语言则允许任意的数据隐式类型转换。**示例如下:
// (JavaScript)弱类型 - 数据会做隐式的类型转换
console.log('100' - 50) // 50
console.log(Math.floor(true)) // 1
## (Python) 强类型 - 数据不会做隐式的类型转换
print('100' - 50) ## TypeError
abs('foo') ## TypeError
需要注意的是,强类型是从语言的语法层面就限制了不允许传入不同类型的值,如果我们传入不同类型的值,我们在编译阶段就会报出错误,而不是运行阶段通过逻辑判断手动抛出,因此 JavaScript 是弱类型的,Python 是强类型的。变量类型允许随时改变的特点不是强弱类型的差异,Python 中的变量就允许随时修改类型,但它是强类型的。
- 类型检查 - 静态类型与动态类型
静态类型最主要的表现就是:一个变量声明时类型是明确的,且声明过后,它的类型就不允许再修改。动态类型语言的特点则是:运行阶段才能够明确变量类型,而且变量的类型可以随时发生变化(还有一种说法是动态类型语言中变量是没有类型的,变量中存放的值是有类型的)。 示例如下:
var foo = 100 // 运行至此时才明确 foo 是一个 number
foo = 'bar' // ok 变量类型可随时发生变化
console.log(foo)
- 弱类型的问题
弱类型语言只能在运行阶段才能够发现代码中存在的类型异常,而如果在测试时也没有运行到那行代码,那么这行代码带来的隐患就会留在代码当中。
// 示例 1
const obj = {}
const a = false // or true
if(a){
obj.foo() // TypeError
}
假设上述代码测试时遗漏了 a 为 true 时的情况,那么第 4 行未运行到,该行隐患就会保留至生产环境。
示例 2
function sum (a, b) {
return a + b
}
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // 100100
上述代码中的 sum 函数因为传入参数类型的不同导致函数的功能发生改变,这就是类型不确定造成的最典型的问题,我们可以通过互相约定来规避这个问题,但约定是根本没有保障的,多人协同开发时我们很难保证所有人能够按照约定去开发。
// 示例 3
const obj = {}
obj[true] = 100
console.log(obj['true']) // 100
我们知道在对象中属性名只能是字符串或 Symbol 值,而我们却可以在 JavaScript 中给予对象的属性名任意类型,在内部对象会将其自动转换为字符串。
上述共叙述了三个弱类型会导致的问题,因此我们在前端开发时定然不能仅限于约定:君子约定有隐患,强制要求有保障。
- 强类型的优势
- 错误更早暴露。编码阶段能够提前消灭一大部分有可能会出现的类型异常,不用等到运行阶段才发现。
- 代码更智能,编码更准确。JavaScript 的智能提示很多时候是没有办法准确提示出有用的内容,很多时候是无法推断我们声明的变量究竟是什么类型的。强类型语言则时时刻刻都能够知道我们的变量是什么类型的,推断提示自然会更准确,开发时更有效率。
- 重构更牢靠。在重构代码时我们总会去修改或删除一些代码,在 JavaScript 中去这样做我们无法保证重构后的代码是否存在其它引用导致异常发生。强类型的话在重构后的编译时就会发现这种异常。
- 减少不必要的类型判断。
2. JavaScript 类型系统的问题
- JavaScript 的任性和不靠谱
由于 JavaScript 是一门弱类型且动态类型的语言,所以它在语言层面的类型系统是非常薄弱的,甚至可以说 JavaScript 根本就没有类型系统。用一个词来形容这种语言就是:任性…,因为它几乎没有任何的类型限制,所以 JavaScript 是及其灵活多变的,而恰因为此,JavaScript 就缺失了类型系统带来的优势:可靠性,简单说就是:不靠谱…。
- 为什么 JavaScript 不能够是强类型/静态类型语言
首先,从 JavaScript 诞生背景来说,早前的 JavaScript 根本就没想过会有发展到如今的规模,最早的 JavaScript 需求都很简单,如果那个时候去做一个类型系统就会显得很多余很麻烦。
其次,JavaScript 是一个脚本语言,脚本语言的特点就是不需要编译,所以即使把它设计成静态语言那也没有什么意义,因为静态语言需要在编译阶段去做类型检查,而 JavaScript 根本没有编译这个环节。
- 为什么现在又需要一门强类型/静态类型的语言
现如今的前端应用遍地开花,全都是大规模的应用,JavaScript 代码变的越来越复杂,开发周期也变的越来越长,那么早期 JavaScript 在小规模应用下的弱类型/动态类型的优势在现如今就变成了它的短板不足。
3. Flow 介绍 - JavaScript 的类型检查器
2014 facebook 推出的一款弥补 JavaScript 弱类型弊端的工具。在 React/Vue 中我们都能够看到 Flow 的使用,足以见得 Flow 是一个非常成熟的技术方案。
它的原理就是让我们在代码当中通过添加一些类型注解的方式来去标记代码中每个变量或参数是什么类型的,Flow 就可以根据这些注解来检查代码当中是否存在类型使用上的异常,从而实现在开发阶段对类型的检查。
我们可以在运行代码前通过使用 Babel 或 Flow 提供的方法来自动剔除代码中额外的类型注解。