类型系统
类型安全方面区分
- 强类型(有更强的类型约束,语言层面限制,在编译阶段进行逻辑判断)
- 实参类型要和形参类型相同
- 一般来说强类型中不允许任意的隐式类型转换
- 弱类型(几乎没有什么类型约束)
- 不限制实参类型
- 一般来说允许任意的隐式类型转换
类型检查方面区分
- 静态类型
- 一个变量声明时它的类型就是明确的,声明过后它的类型就不允许再修改
- 动态类型
- 运行阶段才能够明确的变量类型,而且变量的类型随时可以改变
强类型的优势
- 错误能更早的暴露
- 代码更智能,编码更准确
- 重构更牢靠
- 减少不必要的类型推断
Flow
JavaScript的类型检查器,代码当中通过注解的方式来进行类型检测。
安装Flow:
npm install flow-bin --save-dev
node命令行使用flow:
yarn flow
使用示例:
// 需要文件顶部添加flow标识
// @flow
function sum(a: number, b: number) {
return a + b
}
sum(1, 2) // 正确
sum('1' + '2') // 提示错误
flow只是我们在编写代码时帮助我们进行监测的工具,在实际执行时如果不移除注解会提示语法错误,所以我们需要在运行时将注解移除,我们可以使用flow-remove-types来移除。
安装flow-remove-types:
npm install flow-remove-types --save-dev
使用flow-remove-types:
// 将src目录下的js文件使用flow-remove-types去掉注解,dist为js输出目录
yarn flow-remove-types src -d dist
除了flow-remove-tyeps之外我们还可以使用babel来去除注解
安装babel:
yarn add @babel/core @babel/cli @babel/preset-flow --save-dev
使用:
yarn babel src -d dist
以上我们使用的是命令行转换工具,但是我们在开发过程中希望的是编写代码过程就能看到提示,那我们可以使用VSCode的插件Flow Language Support
类型推断
前边我们说过在js文件的顶部添加**// @flow标识可以让flow帮助我们使用注解去监测问题。其实flow还有另一个功能,那就是类型推断**,即使我们不添加注解,flow也可以根据我们表达式去做类型判断进行监测。
示例:
// @flow
function square(n) {
return n * n
}
// 此处我们传入字符串格式参数时,flow会根据我们返回表达式(求平方)去推断参数应该是数值类型,此时flow会给我们提示错误信息
square('2')
类型注解
虽然flow的类型推断可以帮助我们去进行类型推断,但是添加注解能够帮助我们在后续去理解代码,所以建议使用类型注解。
示例:
// 接收数据
let num: number = 100
// 函数参数
function foo(a: number) {}
// 函数返回值
function foo(): number {
return 100
}
// 函数无返回值时
function foo(): void {}
原始类型
示例:
// string
const a: string = 'zhangsan'
// number: 支持数字(10)、Nan、Infinity
const b: number = 10
// boolean: true、false
const c: boolean = true
// null
const d: null = null
// void
const e: void = undefined
// symbol
const f: symbol = Symbol()
数组类型
示例:
// 方式1:使用Array标识arr1是个数组,<number>标识数组中元素类型为number
const arr1: Array<number> = [1, 2, 3]
// 方式2:使用[]标识arr为数组类型,前边的number标识数组中元素的类型
const arr2: number[] = [1, 2, 3]
// 方式3:指定长度的数组(元组)
const foo: [string, number] = ['foo', 10]
对象类型
示例:
// 指定键值个数以及类型
const obj1: { foo: string, bar: number } = { foo: 'string', bar: 10 }
// 可选键值
const obj2: { foo?: string, bar: number } = { bar: 10 }
// 对象默认可以不指定键值个数以及类型
const obj3 = {}
obj3.key1 = 'value1'
obj3.key2 = 10
// 不指定键值个数,但是设置键值类型
const obj4: { [string]: string } = {}
obj4.key1 = 'value1'
obj4.key2 = 'value2'
函数类型
示例:
// 函数返回值类型:有返回值
function foo(): number {}
// 函数返回值类型:无返回值
function foo(): void {}
// 函数参数指定类型:普通数值参数
function foo(a: number) {}
// 函数参数指定类型:函数作为参数
function foo(callback: (string, number) => void) {
callback('string', 10)
}
foo(function (str, n) {
// 无返回值或返回undefined
})
特殊类型
字面量类型,只能使用指定的值。
示例:
// type定义了指定的字面量(联合类型),那么type只能接收着几种类型
const type: 'normal' | 'warning' | 'danger' = 'normal'
// 由上边联合类型可以扩展出
const a: string | number = 'string' // 10
// 我们也可以这样去使用
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 10
如果有时候类型不能够确定,比如值存在时为number类型,不存在是可能为null,也可能为undefined,那我们就可以使用maybe类型。
示例:
const gender: ?number = 10 // undefined 或 null
// 其实这种maybe类型可以使用上边的联合类型表示
const gender: number | null | undefined = 10
Mixed和Any类型
mixed和any类型都可以指任意类型,被mixed或any标记的值可以接受任意类型的数值。那么它俩有什么区别呢?mixed是强类型的,any是弱类型的。也就是mixed标记的值在使用时需要先判断类型,any标记的值可以当做任何类型去使用。
示例:
// mixed类型
function passMixed(value: mixed) {
// 此处value可能是任意类型,但是使用时需要先做判断,否则语言类型检测会报错
if (typeof value === 'string') {
value.substr(1)
}
if (typeof vlaue === 'number') {
value * value
}
}
passMixed('string')
passMixed(10)
// any类型
function passAny(value: any) {
// 此处value不用做类型判断可以直接使用,语言检测层面不会报错
value.substr(1)
value * value
}
passAny('string')
passAny(10)