JavaScript中的类型与类型推断(Flow)

1,075 阅读5分钟

类型系统

类型安全方面区分

  • 强类型(有更强的类型约束,语言层面限制,在编译阶段进行逻辑判断)
    • 实参类型要和形参类型相同
    • 一般来说强类型中不允许任意的隐式类型转换
  • 弱类型(几乎没有什么类型约束)
    • 不限制实参类型
    • 一般来说允许任意的隐式类型转换

类型检查方面区分

  • 静态类型
    • 一个变量声明时它的类型就是明确的,声明过后它的类型就不允许再修改
  • 动态类型
    • 运行阶段才能够明确的变量类型,而且变量的类型随时可以改变

强类型的优势

  • 错误能更早的暴露
  • 代码更智能,编码更准确
  • 重构更牢靠
  • 减少不必要的类型推断

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)

flow支持的其他类型

Flow文档

Flow Type Cheat Sheet类型文档