哈哈,其实只是学习?
可以提高代码的可靠程度
类型系统
强类型和弱类型(类型安全)
强类型有更强的类型约束,而弱类型中几乎没有约束
强类型不允许任意的数据隐式类型转换,而弱类型则允许任意的数据隐式类型转换
语言层面限制
javascript是一门弱类型/动态类型的语言
弱类型的问题
运行阶段才会发现异常,隐式类型转换可能会引发一系列的问题,强类型语法阶段就不能通过
强类型的优势
错误更早暴露–编码阶段,语言就能表现出来
代码更智能,编码更准确
重构更牢靠
减少不必要的类型判断
静态类型与动态类型(类型检查)
是否允许随时修改变量的类型
Flow
javascript类型检查器
快速上手
先要在文件夹中npm init -y
安装flow-bin
,这个是flow的类型检查工具模块
应用flow的文件开头加上注释// @flow
给函数参数加上类型注释
注意: 使用flow的时候,如果你是用的是vs code那么需要在setting=>搜索JavaScript Validate,来关闭这个自动检测工具,安装Flow Language Support
插件
npm
package.json的
function sum(a: number, b: String){} // 这就是类型注解
编译移除类型注解
flow-remove-types
或者@bable/core @bable/cli @bable/preset-flow
两种工具移除注解
以npm/bable为例:
npm install --save-dev @babel/core @babel/cli @babel/preset-flow 然后新建一个.babelrc文件,配置presets { "presets": ["@babel/preset-flow"] } 如果你把你所有的源文件都放在了src目录下,编译输出目录为dist目录,那么可以运行以下命令 ./node_modules/.bin/babel src/ -d dist/ 或者可以在package.json中配置script { "name": "my-project", "main": "lib/index.js", "scripts": { "build": "babel src/ -d dist/", } }
flow开发工具插件
Flow Language Support
Flow的各种类型
Flow 原始类型
和javascript原始类型一致 number
string
boolean
undefined
null
Symbol
bigInt
Flow数组类型
const arr1: Array<number> = [1, 2, 3]; const arr2: number[] = [1, 23, 4]; // 元组 const foo: [string, number] = ['foo', 100];
Flow对象类型
const obj1: { foo: string, bar: number } = { foo: "string", bar: 100 }; const obj2: { foo?: string, bar: number } = { bar: 100 }; // 声明的时候,可以为空对象,但是定义的数据类型还是要遵守 const obj3: { [string]: string | number } = {}; obj3.key1 = "value1"; obj3.key2 = 100;
Flow函数类型
参数类型在参数后面添加类型注解,对于函数的返回值的类型在函数括号后面添加类型注解
// 定义函数的回调函数的参数类型和返回值类型 function foo(callBack:(string,number)=>void){ callBack('100',200) }
Flow特殊类型
// 字面量类型 const a: "foo" = "foo"; const type: "success" | "warning" | "danger" = "success"; // 或类型、联合类型 type StringORNumber = string | number; // 使用type关键字,type相当于给类型做一个别名 ,单独声明一个类型 const b: StringORNumber = 100; const b1: StringORNumber = "100"; // MayBe 类型 const gender: ?number = undefined; const gender1: ?number = null; const gender2: ?number = 1000;
Flow Mixed 与 any
mixed 所有类型的联合类型 any 都懂吧,不说了
mixed是强类型的 当定义为mixed类型的参数,没有明确定义为某一种类型,是不能当做那一种类型的方式去使用的
例如: function passMixed(value: mixed){ if(typeof value === 'string'){ value.substr(1) } if(typeof value === 'number'){ value * value } }
而any 是弱类型的
Flow类型小结
了解常见的,具体的可以去查询
运行环境API
const element: ?HTMLElement = document.getElementById("app");
flow官方库中可以找到
TypeScript
属于渐进式的,任何一种js的运行环境都支持,相较于flow,功能更为强大,生态更健全
缺点:
- 语言本身多了很多概念(接口,泛型,枚举等),提高学习成本
- 项目初期,TS会增加一些成本
快速上手
1.安装ts模块,会提供tsc
命令
npm init -y npm i TypeScript --save-dev
2.package.json中
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "tsc": "tsc" },
3.新建一个ts结尾的ts文件,就可以愉快的开发了
4.编译
npm run tsc 文件路径
配置文件
全局安装typescriptnpm i typescript -g
,然后tsc --init
初始化tsconfig.json
文件,生成一个基本的ts配置文件
TS数据类型
原始数据类型
const a:string ="foobar" const b: number = 100 // NAN Infinity const c: boolean = true const d:void = undefined //null const e:null = null const f:undefined =undefined const g :symbol = Symbol()
tsconfig文件中"target": "es2015"
才能使用; "target": "es5"
时,使用symbol 也会报错,所有的es6语法都会报错,因为这个设置使得ts默认使用的标准库是es5
所对应的标准库;如果我们一定要使用”target”: “es5″时,同时也要兼容es6时,这个时候我们就需要使用tsconfig文件中的另一个lib 属性,我们就要来认识标准库的概念
标准库
"lib": ["ES2015"]
当这么设置时,会覆盖所有其他的默认标准库,所以要添加"lib": ["ES2015","DOM"]
,ts中把bom和dom归为一个DOM标准库
标准库就是内置对象所对应的声明文件,我们在代码中使用内置对象,就必须要引用对应标准库,否则ts会找不到对应的类型
TypeScript中文错误消息
如果我们使用的是英文版本的话,tsc --locale zh-CN
命令行输入这个命令,可以使错误消息改成中文
TypeScript作用域的问题
不同文件中声明同名变量时,可能会引起作用域问题,ES module、自执行函数等可以处理作用域问题
TypeScript的Object类型
指的不是普通的对象类型,二十泛指所有的非原始类型,包括对象,数组还有函数等
const foo: object = function () { } // [] //{} // 如果声明普通对象类型,可以使用类似对象字面量语法的方式,也可以使用时接口的方式 const obj: { foo: number, bar: string } = { foo: 123, bar: 'string' }
TypeScript的数组类型类型
const arr1: Array<number> = [1, 2, 3] // Array 泛型 const arr2: number[] = [1, 2, 3]
TypeScript的元组类型
元组就是明确元素数量和元素类型的数组
const tuple :[number,string] =[100,'str']
TypeScript的枚举类型
enum PostStatus { Draft = 0, Unpublished = 1, Published = 2 } enum PostStatus1 { // 不指定,默认从0开始累加 Draft, Unpublished, Published } enum PostStatus2 { // 指定第一个,默认从这个基础上开始累加 Draft = 6, Unpublished, Published } enum PostStatus3 { // 字符串枚举的无法累加,要一一指定 Draft = 'qqqq', Unpublished = "wwww", Published = "eeee" }
常量枚举
const enum PostStatus { // 就是加一个const Draft = 0, Unpublished = 1, Published = 2 }
TypeScript的函数类型
就是对函数的输入输出进行类型的限制
函数的声明
// 函数声明 function func1(a: number, b?: number, ...rest: number[]): string { // ?可选参数,和默认参数 都要放在参数最后 ...rest 接受任意个数的参数 return 'func1' } func1(100, 200, 300)
函数表达式
// 函数表达式 const func2: (a: number, b: number) => string = function (a: number, b: number): string { return 'func2' }
TypeScript的任意类型
any,动态类型,有类型安全问题,在运行过程中可以存放其他的值,意思是声明的赋值是number类型,使用的时候赋值了一个string类型的值,是不会报错的,应为TypeScript不会对any 类型的值去做类型检查
TypeScript的隐式类型推断
let age = 18 // 就会推断age为number类型 let age // 就会推断为any 类型
TypeScript类型断言
as 语句
// 假定这个nums 来自一个明确的接口 const nums = [100, 120, 130, 140] const res = nums.find(i => i > 0) // ts推断res: number | undefined // 在后面的操作中 可以用as来断言 res一定是一个number 不会是undefined const num1 = res as number // 或者 const num2 = <number>res // JSX中,<number>会跟html标签冲突,不能使用
TypeScript的接口 interface
接口就是用来约束对象的结构,一个对象实现一个接口,就必须拥有这个接口中定义的所有的成员
编译完成后的文件,没有任何关于接口相关的代码
ts中的接口是为我们有结构的数据做类型约束的,在实际运行阶段是没有意义的
interface Post { title: string content: string } function printPost(post: Post) { console.log(post.title); console.log(post.content); }
接口成员 可选成员、只读成员、动态成员
interface Post { title: string content: string subtitle?: string // 可选成员 readonly summary: string // 只读成员,不能修改 } interface Cache { // 动态成员定义 [key :string]: string // 定义的时候不知道类型,可以这么定义,这里键值都是string类型 } // 举例 const cache : Cache = {} cache.foo = 'value1'
TypeScript的类的使用
类的作用 描述一类具体事物的抽象特征
类 用来描述一类具体对象的抽象成员
TS增强了class的相关语法
// 类的属性在使用之前必须要在类型中去声明,目的是给属性做类型标注 class Person { name: string age: number = 18 // 可以在这里赋值一个初始值,但是一般我们都是在构造函数中动态的去赋值,ts中类的属性必须要有一个初始值 constructor(name: string, age: number) { // 在ts中需要在类中声明某个属性 this.name = name } sayHi(msg: string): void { console.log(`I am ${this.name},${msg}`); } }
类的访问修饰符
class Person { name: string pri age: number = 18 // 可以在这里赋值一个初始值,但是一般我们都是在构造函数中动态的去赋值,ts中类的属性必须要有一个初始值 constructor(name: string, age: number) { // 在ts中需要在类中声明某个属性 this.name = name } sayHi(msg: string): void { console.log(`I am ${this.name},${msg}`); } }
访问修饰符作用: 控制类当中成员的可访问级别
class Student extends Person { private constructor(name: string, age: number) { super(name, age) console.log(this.gender); } static create(name: string, age: number){ return new Student(name, age) } } 这里由于constructor被private修饰为私有的,所以不能通过new Student() 创建新对象的,只能通过在类的内部创建一个静态方法然后通过 Student.create("jack",18)创建对象 // protected 修饰的constructor也是不能被实例化的 相较于 private ,protected修饰的属性是允许被继承的
只读属性
readonly
class Person { public name: string // ts中默认就是public private age: number = 18 //private是私有属性的,只能在类的内部访问 protected readonly gender: boolean // 只允许在子类中访问修饰的成员 只读 constructor(name: string, age: number) { // 在ts中需要在类中声明某个属性 this.name = name this.gender = true } sayHi(msg: string): void { console.log(`I am ${this.name},${msg}`); } }
TypeScript的类与接口
接口中定义方法,只做对于方法的约束,而不做具体实现
interface Eat { eat(food: string): void } interface Run { run(distance: number): void } class Person implements Eat,Run{ eat(food: string) { } run(distance: number) { } } class Animal implements Eat,Run{ eat(food: string) { } run(distance: number) { } }
TypeScript的抽象类 包含具体的实现
//定义抽象类,就是在class前面加上 abstract ,被定义为抽象类之后,只能被继承,不能通过new 创建实例对象了 abstract class Animal { eat(food: string): void { console.log(`呼噜呼噜吃:${food}`); } abstract run(distance: number): void // 抽象方法,不需要具体的方法体,不需要方法实现 } class Dog extends Animal { run(distance: number): void { console.log('runrunrunrun'); } }
TypeScript的泛型
声明函数的时候不去指定类型,到调用的时候传递具体类型,目的是极大程度的复用代码
// 常规操作 function createNumberArray(length: number, value: number): number[] { const arr = Array<number>(length).fill(value) return arr } const res = createNumberArray(3, 100) // 泛型这么用 function createArray<T>(length: number, value: T): T[] { const arr = Array<T>(length).fill(value) return arr } const res1 = createArray<string>(3,'foo') // 使用的时候定义类型
TypeScript的类型声明
当第三方npm 模块不一定是用ts写的,所以不一定有强类型的体验,比如安装lodash,这个时候 我们需要安装npm i @types/lodash --dev
import { camelCase } from 'lodash' declare function camelCase(input: string): string const res = camelCase("hello world")
总结:在ts中,我们需要引入第三方模块的时候,如果这个模块中不包含对应的类型声明文件,我们就要去尝试安装对应的类型声明模块,一般是@types/模块名
,如果没有对应的模块的话,这个时候就要我们手动的使用declare
语句去声明对应的模块类型