类型概述
强类型与弱类型
- 强类型与弱类型 (类型安全)
- 静态类型与动态类型 (类型检查)
强类型语言成面限制函数的实参类型必须与形参类型相同,强类型有更强的类型约束,而弱类型中几乎没有什么约束;
强类型中不允许有任何弱类型转换,而弱类型语言则允许任意的数据隐式类型转换;
强类型从语言的语法层面就限制了不允许传入不同类型的值,如果我们传入不同类型的值,在编译阶段就会报出错误,而不是运行阶段通过逻辑判断进行限制;
静态类型与动态类型
- 静态类型:一个变量声明时他的变量就是明确的,并且声明过后它的类型就不会被修改了;
- 动态类型: 运行阶段才能够明确变量类型而且变量类型可以随时发生变化
// 动态类型
var foo = 100 // int
foo = 'bar' // str is ok
foo = true // boolean is ok
动态类型中,变量是没有类型的,而变量中存放的值是有类型的;
综上:Javascript 是一门弱类型的动态语言;
弱类型语言的问题
弱类型的特点:缺失了类型的可靠性,让程序更加不可控;
const obj = {}
obj.foo()
// console.log(TypeError: obj.foo is not a function)
弱类型语言运行异常要到运行的时候才能检查到;
const sum = (a, b) => a + b
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // "10000"
弱类型语言因为类型不确定,造成程序输出结果异常,造成函数功能发生改变;
强类型语言的优势
- 错误更早暴露,编码阶段就可以检测出大部分的类型异常;
- 代码更智能,编码更准确;
- 重构更牢靠
const utils = {
test: () => {
//...
}
}
程序中多次用到 utils.test 方法,重构中更改了此方法名时,弱类型语言只有在运行某个文件时才会抛错,会造成某种成都的替换错漏,造成未发现的程序异常;强类型语言编码阶段就会在不同文件中抛出异常;
- 减少不必要的类型判断
Flow
起步
JavaScript 的类型检查器,为 JavaScript 代码提供类型注解的方式去标记每一个变量或者是参数应该是什么类型的,Flow 会根据类型注解去检查类型使用上的异常,从而避免了在运行阶段才发现程序异常;
const sum = (a: number, b: number) => a + b
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // TypeError
参数 : 类型 的方式就叫做类型注解;
const sum = (a: number, b) => a + b
console.log(sum(100, 100)) // 200
console.log(sum(100, '100')) // '10000'
Flow 不要求给每一个变量添加注解,可根据需要差异化添加;
Flow 只是一个小工具,不是一门新语言!!!
快速上手
// 安装yarn
curl -o- -L https://yarnpkg.com/install.sh | bash
// 初始化 package
yarn init --yes
// 安装项目依赖
yarn add flow-bin --dev
// 初始化.flowconfig
yarn flow init
// 运行flow 命令
yarn flow
// 结束 flow 命令
yarn flow stop
使用 flow: (flow-test.js)
// @flow
const sum = (a: number, b: number) => a + b
sum(100, 100)
sum(100, '100')
// @flow标记需要 flow 类型检查;a: number类型注解- 运行 flow 命令
yarn flow
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ flow/flow01.js:7:10
Cannot call sum with '100' bound to b because string [1] is incompatible with number [2]. [incompatible-call]
[2] 3│ const sum = (a: number, b: number) => a + b
4│
5│ sum(100, 100)
6│
[1] 7│ sum(100, '100')
编译移除注解
flow 类型注解在运行过程中,会报出异常,导致程序无法运行;所以需要通过命令在运行之前移除注解;移除类型注解有两种方案:
- 使用官方插件:
flow-remove-types
// 安装依赖
yarn add flow-remove-types --dev
// 运行移除命令
yarn flow-remove-types src -d dist
src 表示输入目录为当前目录; -d dist 指定输出目录;
运行结果:(dist/flow01.js)
const sum = (a , b ) => a + b
sum(100, 100)
sum(100, '100')
- 使用
babel插件
// 安装依赖
yarn add @babel/core @babel/cli @babel/preset-flow --dev
// 运行移除命令
yarn babel src -d dist
.babelrc
{
"presets": ["@babel/preset-flow"]
}
运行结果:
const sum = (a, b) => a + b;
sum(100, 100);
sum(100, '100');
Flow 开发工具插件
上面介绍的方式,是需要用命令行才能触发 flow 类型检查,用 VSCode 插件配合校验--VScode 插件:Flow Language Support
类型推断
flow 可以根据代码中的变量使用情况推断出 变量的基本类型;
类型注解
类型注解不仅可以用于传入参数,还可以用于定义变量,定义函数返回值;
const square = (n: number): number => n * n
const printSquare = (n: number): void => {
console.log(n * n)
}
let count:number = 0
原始类型
const a: string = 'str'
const b: number = Infinity // NAN / 100
const c: boolean = false
const d: null = null
const e: void = undefined
const f: symbol = Symbol()
数组类型
// 泛型
const arr1: Array<number> = [ 1, 2, 3 ]
// 元组 - 一个函数中同时要返回多个返回值时,可以返回这种元组类型
const arr2: [string, number] = ['age', 19]
//
const arr3: number[] = [ 1, 2, 3 ]
对象类型
// 必传参数
const obj1: { name: string, age: number } = { name: 'nick', age: 19 }
// 可选参数
const obj2: { name: string, age: number, money?: number } = { name: 'nick', age: 19 }
// 动态参数
const obj3: {[string]: string } = {}
obj3.name = 'nick' // success
obj3.age = 100 // error
函数类型
// 高阶函数
const foo = (cb: (string, number) => void) {
cb('age', 100)
}
特殊类型
- 字面量类型: 限制变量必须时某一个值
// 固定值
const a: 'foo' = 'foo' // 其他值都会报错
// 联合类型
const type: 'success' | 'warning' | 'danger' = 'success' // 其它值会报错
// 参数或类型
const idType = string | number | null
const id: idType = null // 其它值会报错
- maybe 类型
const gender: ?string = '未知' | null | undefined // 可能存在为字符串类型
// 等价于
const gender: string | null | undefined = 未知' | null | undefined
- Mixed & Any 他们都可以用来表示,六种基本类型的联合类型:
const passMixed = (value: mixed) => console.log
或
const passMixed = (value: any) => console.log
passMixed('hello') // success
passMixed(true) // success
passMixed(100) // success
...
不同点: Mixed 类型是一个强类型,Any 类型是一个弱类型:
// 编码阶段报错
const passMixed = (value: mixed): void => {
// value 没有明确类型,所以不能默认类型运算
console.log(value * value) // number
}
// 编码阶段不报错
const passMixed = (value: mixed): void => {
if (typeof value === 'string') {
console.log(value.strstr(1)) // string
} else if (typeof value === 'number') {
console.log(value * value) // number
}
}
// 编码阶段不报错
const passMixed = (value: any): void => {
// value 没有明确类型,所以可以默认类型运算
console.log(value * value) // number
console.log(value.strstr(1)) // string
}
any 存在的意义,兼容老代码;尽量使用 mixed;
类型小结
官方文档:
https://flow.org/en/docs/types/
第三方类型查询文档
https://www.saltycrane.com/cheat-sheets/flow-type/latest/
运行环境 API
浏览器环境或者 Node 环境会根据不同环境,进行接口限制:
// 浏览器中
const element: HTMLElement | null = document.getElementById('app')
一些其它环境的Flow类型限制文件: