强类型和弱类型
- 从类型安全的纬度区分 分为强和弱, 最开始是在函数参数中加类型限制, 基础理解: 强类型 不允许隐式类型转换 弱类型允许
静态类型和动态类型
- 从类型检查的纬度,分为静态类型和动态类型, 是否允许变量申明后去修改变量值的类型
javascript是一个弱类型、 动态类型的语言,是一个任性的语言,失去了类型的约束是一个不靠谱的语言。
- javascript是一个没有编译环节的是一个脚本语言
弱类型的一些问题
- 必须要等到运行时才能发现语法异常
- 类型不明确会造成函数功能发生异常
- 隐式转换规则不清晰
强类型的一些优势
- 错误可以提前暴露
- 代码更智能、编码更准确
- 重构会更牢靠
- 减少代码层面不必要的类型判断
Flow
Flow javascript静态类型检查器
- 通过类型注解来做类型检查
flow的基础使用
- flow-bin 是flow的安装包,使用npm安装
- 在需要flow检查的文件顶部加 @flow的注释,用来标识该文件需要flow来检查静态类型
- 在文件代码中加入类型注解
- 使用 yarn flow命令进行检查
在编译阶段移除类型注解
- 安装 flow-remove-types
- 执行命令 yarn flow-remove-types ./src -d dist 编译src目录下的代码,删除类型注解 到dist目录
- babel中也有babel-flow插件可以删除类型注解
vscode中可以安装 flow-language-support 可以在编码阶段给出代码错误提示
flow的类型推断
function square(n) {
return n * n
}
square('100') //这个时候flow就会提示类型错误,因为字符串是不能进行*运算的
类型注解(尽量添加类型注解、少交给类型推断)
const no: number = 1
function foo(): string { return '' }
function foo(): void { }
flow支持的原始数据类型
- string number(NaN、 Infinity都是属于number类型) null undefind(需要使用void表示) Object Array Symbol
数组类型
const arr: Array
const arr: number[] = [1,2,3,4,5]
const arr:[string , number] = ['foo', 1] //元祖, 一般函数返回多个不同类型的值的时候使用
对象类型
const obj: {foo?: string, bar: number} = {foo: 'string', bar: 100} //?表示可选
const obj: {[string]: string} = {} //允许添加多个字符串键,和value是string的值
函数类型
- 函数主要限制参数类型 和返回值类型类型
- 函数作为参数传递
function foo( callback: (string, number) => void) { callback()}
特殊情况
const type: 'success' | 'warning' | 'danger' = 'danger'
type stringOrNumber = string | number = 123
const type1:stringOrNumber = 123
const type2:?number = null // 在number类型上扩展了 null和undefined
mixed和any类型,可表示任意类型
- 区别 mixed还是强类型(不能直接使用字符串方法或数字方法) any还是一个弱类型(可以调用任意类型的方法)
内置API的检查
const app: HTMLelement | null = document.getElementById('app')
TypeScript
TypeScript是javascript的超集,比JavaScript多了一个类型检查系统和对es新特性的完全支持,最终会被编译成js应用,可以兼容到es3
TypeScript可以说是前端领域的第二语言,现阶段react、angular、vue3 都使用了。
缺点:
- 本身多了很多概念(接口、泛型、枚举等等)
- 对于小项目,typescript会增加很多类型申明的成本
ts基本使用
- 安装typescript模块
- 模块提供了tsc命令, tsc命令会编译ts文件到js(移除类型注解、编译最新的语法)
tsconfig
- yarn tsc --init
- 修改配置文件选项后,可直接执行yarn tsc进行ts的语法编译
ts基本类型
- ts基本类型的使用和flow是一样的,需要注意的是标准库申明文件的使用(例如symbol是es2015申明文件里面申明的,而console是浏览器对象BOM里面提供的, DOM和BOM的申明文件都在DOM里面)
错误提示可以主动设置
- 编译时加上 --local zh-CN
- 编译器vscode中,在设置中搜索typescript的,可以语言就可以了
不同文件中命名冲突
- 文件使用const等申明后是在全局作用下,那么在下一个文件中使用相同命名就会提示冲突,解决方案:
- 1.加上立即执行函数,把作用域编程函数作用域
- 2.添加 export {} 将当前文件编程一个模块,变成一个局部作用域
Object类型
- object不单指{}, 也可以是数组和函数
- 使用字面量写法的时候,需要值和申明需要完全一致,不能多也不能少。
const obj:{foo: string} = {foo: '1234', more: 123} // 这个时候more:123就会报错
数组类型和flow使用一致
const arr: Array
const arr: number[] = [1,2,3,4,5]
元祖类型
- 可以理解为固定长度、固定元素类型的数组
枚举类型
某个值固定只能使用某几个值
枚举类型会入侵我们代码,简单来说,就是会改变我们编译后代码
-
加了const就会变成常量枚举
const enum statusProp { start = 0, ing = 1, end = 2 } const status: 0|1|2 = 1 const status2 = statusProp.start
函数的类型约束
function fn (a: number, b: number) :string {
return 'abc'
} //参数的形参和实参必须保持一致
fn(1,3)
// fn(1,3,2) // 2就会报错
任意类型--typescript不会是any类型的数据做类型检查
function stringify(value: any) {
return JSON.stringify(value) //stringify可以接收任何类型的参数
}
隐式类型推断
- 如果我没有写类型注解,typescript会根据我们写的代码来推断数据的类型
let age = 18; age = 'string' //这个时候就会报错,ts推断age为number类型
类型断言
有时候typescript无法推断出一个变量的类型,作为开发人员我们可以主动的判断出是什么类型。那么我们就告诉typescript这个变量的类型
const arr = [1,2,3,5,6]
const res = arr.find((e) => e>0)
const res1 = res as number
const res2 = res //在jsx环境下会和标签产生冲突
const num = 100 - res1
接口(可以理解为一个规范或者约定)interface 规范一个对象所拥有的属性
interface Content {
title: string
message: string
desc?: string
readonly btn: string
}
function printContent(content: Content) {
console.log(content.title)
console.log(content.message)
}
printContent({
title: "haha",
message: "this is"
})
// 动态类型
interface Cache {
[key: string]: string
}
const cache: Cache = {}
cache.foo = "1234"
类(描述一类具体事物的抽象特征)以及类的修饰符
class Person {
name: string
private age: number //私有属性
protected gender: boolean //对子元素public
constructor(name: string, age: number) {
this.name = name
this.age = age
this.gender = true
}
say (msg: string) :void {
console.log(`my name is ${this.name}`)
}
}
const ton = new Person('ton', 18)
ton.name
ton.age //私有属性无法访问
class Student extends Person {
constructor(name: string, age: number) {
super(name, age)
console.log(this.gender)
}
}
类与接口
接口约定了类必须实现的方法
interface EatAndRun {
eat(food: string): void
run(distance: number): void
}
interface Eat {
eat(food: string): void
run(distance: number): void
}
interface Run {
run(distance: number): void
}
class Person1 implements EatAndRun {
eat(food: string) {
console.log(`慢慢的吃${food}`)
}
run(distance: number) {
console.log(`双脚跑步${distance}`)
}
}
class Animals implements Eat, Run {
eat(food: string) {
console.log(`呼噜呼噜的吃${food}`)
}
run(distance: number) {
console.log(`四脚跑步${distance}`)
}
}
抽象类 (可以约束类的成员,与接口不同的是,可以包含一些成员的实现)一般大的类,我们会使用抽象类
abstract class Animal { //使用类抽象类之后,该类不能使用new来实例化
eat(food: string) {
console.log(`呼噜呼噜的吃${food}`)
}
abstract run(distance: number): void //抽象方法
}
class Dog extends Animal {
run(distance: number): void {
console.log(`4脚爬行${distance}`)
}
}
const dog = new Dog()
dog.eat('gutou')
dog.run(100)
泛型
我们在定义函数、接口、类的时候我们没有定义类型,在调用的时候传入类型
function creatNumArray(len: number, value: number): number[] {
return Array(len).fill(value)
}
function creatStrArray(len: number, value: string): string[] {
return Array(len).fill(value)
}
function creatArray(len: number, value: T): T[] {
return Array(len).fill(value)
}
const arrs = creatArray(10, '2')
const arrn = creatArray(10, 1)
类型声明 declare
引入第三方模块的时候,函数或者类没有对参数进行限制,我们需要手动添加声明,或者安装对应的声明模块