强类型与弱类型(类型安全)

  • 强类型:语言层面限制函数的实参类型必须与形参类型相同。如Java语言
  • 弱类型:语言层面不会限制函数的实参类型与形参类型一定要相同,如JavaScript

强类型中不允许有任意的隐式类型转换,而弱类型是允许的 强类型语言在编译阶段就能报类型不符合要求的错误

静态类型与动态类型(类型检查)

  • 静态类型:
    • 一个变量声明时它的类型就是明确的。
    • 变量声明后,类型不允许修改。
  • 动态类型:
    • 变量在运行时才能确定类型。
    • 变量的类型可以发生变化。

JavaScript类型系统特征

  1. JavaScript是弱类型且是动态类型的语言,比较任性,缺失类型系统的可靠性。
  2. 这是历史遗留的问题,没有编译环节,早期这种模式是比较有优势的。
  3. 但现如今JavaScript被大规模的应用,这种模式成了劣势。

弱类型的劣势

  1. 必须等运行时候才能发现类型的异常,开发测试时候不容易发现类型的问题。
  2. 多人协同开发的时候出异常概率更大。
  3. 类型不明确会导致函数功能异常。
  4. 类型随意转换会带来隐藏的bug。

强类型的优势

  1. 错误更早暴露。
  2. 代码更智能,编码更准确。
  3. 重构更牢靠。
  4. 减少不必要的类型判断。省去了手动判断类型的操作。

Flow

JavaScript的类型检查器,FaceBook开源的

  1. // 类型注解,可以使用babel编译去除注解
  2. // @flow 用于flow命令检查
  3. function sum(a: number, b: number){
  4. return a + b
  5. }
  6. // yarn add flow-bin --dev开发依赖
  7. // flow init 初始化.flowconfig文件
  8. // flow 开始类型检查

使用工具来移除注解(flow)

  1. flow-remove-types: flow-remove-types src -d dist.
  2. babel: 安装@babel/core @babel/cli @babel/preset-flow。然后添加.babelrc配置文件。运行babel src -d dist命令。
    1. // .babelrc文件内容
    2. {
    3. "presets": ["@babel/preset-flow"]
    4. }

Flow开发工具插件

Flow 类型推断

  • 根据代码使用情况,推断代码中的类型,提示错误。

Flow 类型注解

  1. // 函数参数的标注
  2. function square (n: number) {
  3. return n * n
  4. }
  5. // 变量的类型标注
  6. let num: number = 100
  7. // 函数返回值类型的标注,如果函数没有返回值 标注void
  8. function foo (): number{
  9. return 100
  10. }

Flow 原始类型

JavaScript中原始数据类型: String Number Boolean Null undefind Symbol

  1. const a: string = 'foo'
  2. const b: number = 100 // NaN // Infinity
  3. const c: boolean = true
  4. const d: null = null
  5. const e: void = undefined
  6. cosnt f: symbol = Symbol()

数组类型

  1. // 需要使用泛型标注数组每一位是什么类型的值
  2. const arr1: Array<String> = ['foo','str']
  3. const arr2: number[] = [1, 2, 3]
  4. // 表示固定长度数组 也称元组
  5. const foo: [string, number] = ['foo', 100]

对象类型

  1. const obj1: { foo: string, bar: number } = {
  2. foo: "foo",
  3. bar: 12
  4. }
  5. // 可选参数
  6. const obj1: { foo?: string, bar: number } = {
  7. bar: 12
  8. }
  9. // 当前对象允许添加任意类型键值,但是键值必须是字符串类型
  10. const obj: { [string]: string } = {}
  11. obj.key1 = "foo"

函数类型

  1. function foo(callback: (string, number) => void) {
  2. callback()
  3. }
  4. foo(function("str", 100){
  5. })

特殊类型

  1. // 直接标注字面量 通常配合联合类型使用
  2. const a: 'foo' = 'foo'
  3. const type: 'success' | 'warning' | 'danger' = 'success'
  4. const b: string | number = "123" //123
  5. type StringOrNumber = string|number
  6. const b: StringOrNumber = 'string'
  7. // maybe 有可能
  8. const gender: ?number = null // undefined // 123
  9. const gender: number| null | undefined

任意类型

  • mixed 强类型
  • Any 弱类型
  1. function passMixed (value: mixed) {
  2. // 如果进行了多种类型的运算或者调用了多种类型的方法 会报警告
  3. }
  4. function passAny (value: any){
  5. //
  6. }

类型手册

Flow 运行环境API

浏览器环境的DOM,BOM。node中的fs等

  1. const element: HTMLELement | null = document.getElementById('app')

flow运行环境API源代码

TypeScript

JavaScript的超集

  • 包含了JavaScript,类型系统和ES6+。
  • 能编译到低版本浏览器中运行,最低编译到ES3。
  • 功能更强大,生态也更健全,更完善。
  • Angular/Vuejs3.0

缺点:

  1. 语言本身多了很多概念,例如:枚举,泛型,接口等概念。不过好在TS也是渐进式的,上手快进阶快。
  2. 项目初期,TypeScript会增加一些成本,比如需要编写很多类型。但对于长期维护的大型项目,带来的优势远大于这些劣势。

基础用法

  1. // yarn add typescript --dev
  2. // tsc命令 帮助编译ts代码
  3. // TypeScript文件扩展名默认是.ts
  4. // 类型标注与Flow类似
  5. const hello = (name: string) => {
  6. console.log(`hello,${name}`)
  7. }

配置文件

  • 编译工程的时候使用 ``` tsc —init // 初始化tsconfig.json 配置文件

CompilerOptions中的常用属性: target属性:表示编译后的ES版本 module属性:表示编译完遵循什么样的模块化规范 outDir属性:表示编译输出的目录 rootDir属性:表示的是TS源代码的文件夹 sourceMap属性: 表示开启源代码映射,可以在编译后调试TS源代码 strict:开启严格检查模式,需要明确指定类型

直接运行tsc才会根据配置文件编译

  1. <a name="c410d590"></a>
  2. ## 原始数据类型的标注
  3. ```javascript
  4. const a: string = 'foobar'
  5. const b: number = 100
  6. const c: boolean = true
  7. // 上面三个与flow不同的是 这些标注允许值为空(null或者undefined,关闭严格模式才允许)
  8. const d: void = undefined
  9. const e: null = null
  10. const f: undefined = undefined
  11. const g: symbol = Symbol() // 需要将tsconfig.js中target修改为es2015 或者在lib中增加上["ES2015", "DOM"]

TS显示中文错误消息

  • tsc —locale zh-CN
  • VSCode中配置文件里面搜索typescript locale将Locale设置为zh-CN

TS中作用域的问题

  • 可使用立即执行函数
  • 或者在文件中使用 export {}

Object类型

泛指所有非原始类型:对象,数组,函数

  1. const foo: object = function () {} || [] || {}
  2. const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' } //对象标注最好用接口形式

数组类型

  1. const arr1: Array<number> = [1, 2, 3]
  2. const arr2: number[] = [1, 2, 3]

元组类型

  1. const tuple: [number, string] = [12, 'azx']
  2. // 一般用来标注函数返回值

枚举类型

  1. const enum PostStatus {
  2. Draft = 0,
  3. Unpublished = 1,
  4. Published = 2
  5. }
  6. // 如果枚举不指定,就会从0开始累加或者从第一位值开始累加
  7. const post = {
  8. title: "aaa",
  9. status: PostStatus.Draft
  10. }

函数类型

  1. // 函数声明
  2. function func1 (a: number, b?: number, ...rest: number[]): string {
  3. return 'func1'
  4. }
  5. // 函数表达式
  6. const func2: (a: number, b: number) => string = function (a: number, b: number): string {
  7. }

任意类型

  1. function stringify (value: any) {
  2. return JSON.stringify(value)
  3. }
  4. // any 类型是动态类型的

隐式类型推断

  1. let age = 18
  2. //age 会被推断为number 如果推断不了会解析为any

类型断言

  1. // 相当于明确告诉TS是什么类型的
  2. const num1 = res as number
  3. const num2 = <number>res // 如果jsx中使用就会有冲突

接口

规范,约定对象的结构

  1. interface Post{
  2. title: string
  3. content: string
  4. subtitle?: string// 可有可无的
  5. readonnly summary: string // 只读成员
  6. }
  7. function printPost (post: Post) {
  8. console.log(post.title)
  9. console.log(post.content)
  10. }
  11. printPost({
  12. title:"JavaScript高级程序设计"
  13. content: "A JavaScript Book"
  14. })
  15. // 动态成员
  16. interface Cache {
  17. [prop: string]: string // 键值是string类型
  18. }

类(class)

用来描述一类具体对象的抽象成员,TS增强了class的相关语法

  1. class Person {
  2. name: string = 'init name'
  3. private age: number = 18
  4. protected readonly gender: boolean //只读属性
  5. constructor(name: string, age: number, gender: boolean) {
  6. this.name = name
  7. this.age = age
  8. this.gender = gender
  9. }
  10. sayHi (msg: string): void {
  11. console.log(`I am ${this.name}, ${msg}`)
  12. }
  13. }
  14. // 类的成员需要声明并且标注,而且构造函数中也要赋值
  15. // 可使用修饰符来修饰成员变量,越来越像Java了 public private protected
  16. // protected只允许在子类中访问成员
  17. // 构造函数也能被修饰,这样只能使用静态方法调用构造函数
  • 类与接口 ```javascript interface EatAndRun { eat (food: string): void run (distance: number): void }

class Person implements EatAndRun { eat(food){ console.log(food) } run(distance) { console.log(distance) } }

class Animal implements EatAndRun { eat(food){ console.log(food) } run(distance) { console.log(distance) } } // 真·越来越像Java 不过Java中的接口可以约定方法,实现类必须实现方法 // 一个接口约定一种方法 类型 用类实现多个接口

  1. - 抽象类
  2. ```javascript
  3. abstract class Animal {
  4. abstract run (distance: number):void
  5. }
  6. // 子类继承的时候实现这个抽象方法
  7. // 真·真·越来越像Java

泛型

声明函数时不指定类型,调用的时候才指定类型

  1. function createNumberArray (length: number, value: number): number[] {
  2. const arr = Array<number>(length).fill(value)
  3. return arr
  4. }
  5. function createArray<T> (length: number, value: T): T[] {
  6. const arr = Array<T>(length).fill(value)
  7. return arr
  8. }
  9. const res = createNumberArray(3, 100) // [100, 100, 100]
  10. const res1 = createArray<string>(3, "123")

类型声明

第三方库不一定有类型声明文件

  1. // 比如lodash
  2. declare function camelCase(input: string): string
  3. // 或者安装lodash的类型声明文件 @types/lodash