哈哈,其实只是学习📒
可以提高代码的可靠程度
类型系统
强类型和弱类型(类型安全)
强类型有更强的类型约束,而弱类型中几乎没有约束
强类型不允许任意的数据隐式类型转换,而弱类型则允许任意的数据隐式类型转换
语言层面限制
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语句去声明对应的模块类型