TyepeScript、Flow入门教程及强类型弱类型简单阐述

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

    (此处存在争议,以下观点仅代表个人理解) 编程语言分为强类型与弱类型,强类型有更强的类型约束,而弱类型中几乎没有什么约束

    • 强类型语言
      • 在语言层面就限制了函数的实参类型必须与形参类型相同
      • 不允许任意数据的隐式转换
      • 错误更早暴露
      • 代码更智能,编码更准确
      • 重构更加牢靠
      • 减少不必要的类型判断
    • 弱类型语言
      • 在语言层面不会限制实参的类型
      • 语序任意数据的隐式转换
  • 静态类型与动态类型(类型检查)
    • 静态类型语言
      • 一个变量声明时它的类型就是明确的
      • 声明后它的类型不允许在修改
      • 编译是需要做类型检查
    • 动态类型语言
      • 声明的变量是没有类型的
      • 变量中存放对的值是有类型的
  • JavaScript自有类型系统特征
    • 弱类型且动态类型
    • 灵活多变
    • 可靠性低
    • 没有编译环节
// 程序类型异常,必须等待执行时才会报错
const obj = {}
obj.foo()

// 两个数组相加,可能会传入非Number参数
funciton sum (a,d){
    return a + b
}
sum(100,'abc')

//可以使用任意类型作为属性 属性名会自动通过toString转换 
const obj = {}
obj[true] = 100
console.log(obj['true']) // 100
  • Flow静态类型检查器

    2015年由FaceBook提供的一款工具,为JavaScript提供完善的类型检查方案

  • 工作原理

    在代码中通过添加类型注解的方式来标记每一个变量或者参数的类型,flow 根据注解解析类型,从而发现类型错误。

  • 安装

        # 安装
        yarn add flow-bin --dev
        # 初始化
        yarn flow init 
    
  • 新建一个js文件且在最上部通过注释形式写 //@flow

        // @flow
        function sum (a:number,b:number){
            return a + b
        }
        sum(1,2)
        sum('a','b')
    
  • 执行 yarn flow
    TyepeScript、Flow入门教程及强类型弱类型简单阐述_第1张图片

  • flow编译移除注解–flow-remove-types

    # 安装flow-remove-types 
    yarn add flow-remove-types --dev
    # 执行 flow-remove-types
    # . 当前目录  
    # -d 代表 --out-dir 
    # dist 输出目录 
    yarn flow-remove-types . -d dist  
  • 编译后的文件
    TyepeScript、Flow入门教程及强类型弱类型简单阐述_第2张图片

  • flow编译移除注解–babel

    # 安装 @babel/core babel核心模块
    yarn add @babel/core --dev
    # 安装 @babel/cli  babel命令行工具
    yarn add @babel/cli --dev
    # 安装 @babel/preset-flow  babel Flow注解插件
    yarn add @babel/preset-flow --dev
  • 新建 .babelrc
{
    "presets" : [
        "@babel/preset-flow"
    ]
}
    # 执行
    # src 工作目录
    # -d --out-dir  输出
    # dist 输出文件夹
    yarn babel src -d dist

TyepeScript、Flow入门教程及强类型弱类型简单阐述_第3张图片

  • flow + vscode+Flow Language Support 食用更加

  • flow 类型注解

@flow
// 形参类型注解
function sum (a:number,b:number){
    return a + b
}
// 变量类型注解
const num:number = 100
// 返回值类型注解
function foo():number{
    return 100
}
// 返回值为空的类型注解
function baz():void{
    return 100
}
  • flow 原始数据类型注解
const a : string = 'string'
const b : number = Infinity // NaN  Infinity 100 
const c : boolean = true // true false 
const d : null = null 
const e : void = undefined
const f : symbol = Symbol()

// 数组数据类型限制
const arr1: Array < string > = ['a', 'b', 'c']
const arr2: number[] = [1, 2, 3, 4, ]
// 固定长度的数组也可以成为元组数据类型
const arr3: [String, number] = ['a', 100]

// 对象数据类型限制
const obj: {
    foo: string,
    bar: number
} = {
    foo: 'string',
    bar: 100
}
// 可选成员
const obj2: {
    foo: string,
    bar ? : number
} = {
    foo: 'string',
    bar: 100
}
// 对象允许添加任意类型  但是 键只可以为 string 值只能是 number
const obj3: {
    [string]: [number]
} = {}
obj3.boo = 100
obj3.baz = 200

// 函数数据类型限制
function foo({
    num: number
}): number {
    return 100
}
// 设置回调函数据类型限制
function foo(callback: (number) => number): number {
    return callback()
}

// 特殊数据类型
// 字面量类型
const a: 'foo' = 'foo' //只能存放 foo字符串
// 联合变量
const type: 'success' | 'warning' | 'danger' = 'success' //只能存放 'success'|'warning'|'danger'

const b: string | number = 'string' //100

// 类型别名 or 用 type 声明一个类型
const StringOrNumber = string | number
const c : StringOrNumber = 'string' //100 

// mybe类型 
const gender : ?number = null

// Mixed 数据类型 强类型 所有类型的集合
function passMixed(value:Mixed){
    if(typeof value === 'string'){
        value.substr(1)
    }
    if(typeof value === 'number'){
        value * value
    }
}
passMixed(100)
passMixed('string')

// any数据类型 弱类型

function passAny(value:Any){
    value.substr(1)
    value * value
}
passAny(100)
passAny('string')

// 运行环境内置限制
const element: HTMLElement | null = document.getElementById('abc')
  • TypeScript语言规范与基本应用

TypeScript是JavaScript超集,编译过为JavaScript

# 初始话工程
yarn 
# 安装TypeScript
yarn add typescript --dev
  • 简单案例
const fn = (str: string) => {
    console.log(`hello ${str}`)
}
fn('word')
# 编译
yarn tsc 01-test.ts

编译过后可以在当前目录下 看到同名成的.js文件

var fn = function (str) {
    console.log("hello " + str);
};
fn('word');

  • TypeScript 配置文件
# 生成配置文件
yarn tsc --init
# 执行之后会新增tsconfig.json
  • tsconfig.json 常用配置注解
{
  "compilerOptions": {
    "target": "ES5",                          /* 编译后输出版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "commonjs",                     /* 编译后输出js模块化加载方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "outDir": "./dist",                       /* 输出路径.*/
    "rootDir": "./src",                       /* 源代码所在路径 */
    "sourceMap": true,                        /* 开启源代码映射 */
    "strict": true,                           /* 严格模式 */
    "esModuleInterop": true,                  /* Enables emit interoperability between*、
  }
}
  • 目录结构
├── dist
│   ├── 01-test.js
│   └── 01-test.js.map
├── node_modules
├── package.json
├── src
│   └── 01-test.ts
├── tsconfig.json
└── yarn.lock
  • 执行编译
tsc
  • TypeScript原始类型
const a: string = 'string'
const b: number = Infinity // 100 NaN
const c: boolean = true
const d: void = undefined
const e: null = null
const f: undefined = undefined
const g: symbol = Symbol()

// 对象类型 支持数组 对象 函数
const foo: object = {} // [] funciton(){}
const obj: { foo: number, bar: string } = { foo: 124, bar: 'string' }

// 数组类型
const arr1: Array<number> = [1, 2, 4]
const arr2: number[] = [1, 2, 4]

// 元组类型
const tupple: [number, string] = [18, 'string']

// 枚举类型
enum PostStatus {
    Padding = 0,
    Success = 1,
    Fail = 2
}
// 如果不赋值那么会自动的从0开始累加
enum PostStatus2 {
    Padding,
    Success,
    Fail
}
console.log(PostStatus2['Success']) // 1
console.log(PostStatus['Success']) // 1

// 函数类型 
// ? 代表可选参数
function func1(a:string, b:number,c?:string):string{
    return `func1 a=${a},b=${b},c=${c}`
}
console.log(func1('a',1) ) // func1 a=a,b=1,c=undefined
console.log(func1('a',1,'c')) // func1 a=a,b=1,c=c

// 通过 = 给与默认值
function func2(a:string, b:number,c:string='默认值'):string{
    return `func1 a=${a},b=${b},c=${c}`
}
console.log(func2('a',1) ) // func1 a=a,b=1,c=默认值
console.log(func2('a',1,'c')) // func1 a=a,b=1,c=c

// 箭头函数方式
const func3 :(a:string, b:number,c?:string)=>string = (a:string, b:number,c:string='默认值')=>{
    return `func1 a=${a},b=${b},c=${c}`
}
console.log(func3('a',1) ) // func1 a=a,b=1,c=默认值
console.log(func3('a',1,'c')) // func1 a=a,b=1,c=c

// 使用 ...解构并检查数据类型
function sum(...args: number[]) {
    return args.reduce((prev, current) => prev + current, 0)
}
sum(1, 2, 3)

// 任意类型参数  any 属于动态类型 ts不会检查
function stringify(value:any):string{
    return JSON.stringify(value)
}
  • TypeScript 隐式类型推断

虽然通过隐式类型推断可以简化一部分代码,但是会降低阅读性。所以不建议通过隐式类型推断出变量类型

const age = 18  //推断为 number 
const name = 'zzy'  //推断为 string
const foo  //推断为 any
// ...
  • TypeScript 类型断言
const nums = [1, 2, 3, 4, 5]
const res = nums.find(i => i > 0) // 推断为 number | undefined
const num1 = res as number // 断言 res的类型一定为 number
const num2 = <number>res // 断言 res的类型一定为 number(尖括号模式在jsx会有语法冲突)
  • TypeScript interface 接口(规范/契约)
interface Post {
    title:string
    content:string
} 
function pringPost(post:Post){
    console.log(post.title)
    console.log(post.content)
}
pringPost({
    title:'zzy',
    content:'hello word'
})
  • TypeScript interface 只读属性,可选属性
interface Post {
    title:string
    content:string
    // 可选成员
    desc?:string
    // 只读属性
    readonly sunmary:string
} 

const post1:Post = {
    title:'Title is post1',
    content:'content is post1',
    sunmary:'sunmary is post1'
}
// 修改普通属性
post1.title = 'update post1 title'
// 尝试修改只读属性
//post1.sunmary='str' // 报错 Cannot assign to 'sunmary' because it is a read-only property.
  • TypeScript interface 动态添加成员
interface Cache {
    // 可以动态添加成员
    [porp:string]:string|number|number[]
}
const obj:Cache = {}
obj.test1='abc'
obj.test2=1234
obj.test3=[1,2,3,4,5]
  • TypeScript interface 接口继承
interface Person{
    name:string
    age:number
}
interface Student extends Person{
    classesId:number
    studentId:number
}

const student:Student = {
    classesId:1,
    studentId:2,
    name:'student1',
    age:18
}
  • TypeScript 类

类的作用描述一类具体事物的抽象特征

  • TypeScript 类 基本属性
class Person {
    name:string
    age:number
    status:boolean=true
    constructor (name:string,age:number){
        this.name=name
        this.age=age
    }
    sayHi(msg:string):void{
        console.log(`${this.name} say ${msg}`)
    }
}
  • TypeScript 类 修饰符

    控制类中的成员可访问级别

    • public 公共属性
    • private 私有属性 只允许在当前类中访问的成员
    • protected 受保护的 允许在子类或当前类中访问的成员
class Person {
    // public  公共属性 
    public name:string
    // private 私有属性  只允许在当前类中访问的成员
    private age:number
    // protected 受保护的 允许在子类或当前类中访问的成员
    protected status:boolean=true
    constructor (name:string,age:number){
        this.name=name
        this.age=age
    }
    sayHi(msg:string):void{
        console.log(`${this.name} status is ${this.status}`)
        console.log(`${this.name} age is ${this.age}`)
        console.log(`${this.name} say ${msg}`)
    }
}
class Student extends Person{
    constructor(name:string,age:number){
        super(name,age)
        console.log(this.name)
        //console.log(this.age) // 报错 Property 'age' is private and only accessible within class 'Person'.
        console.log(this.status)
    }
}
const tom = new Student('tom',18)
  • TypeScript 类 constructor与修饰符

private修饰constructor,则constructor只能通过类上的static方法创建

class Person {
    public name:string
    private age:number
    private constructor (name:string,age:number){
        this.name=name
        this.age=age
    }
    sayHi(msg:string):void{
        console.log(`${this.name} age is ${this.age}`)
        console.log(`${this.name} say ${msg}`)
    }
    static create(name:string,age:number):Person{
        return new Person(name,age)
    }
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody

privateed修饰constructor,则constructor可以通过类上的static方法创建及继承

class Person {
    public name:string
    private age:number
    protected constructor (name:string,age:number){
        this.name=name
        this.age=age
    }
    sayHi(msg:string):void{
        console.log(`${this.name} age is ${this.age}`)
        console.log(`${this.name} say ${msg}`)
    }
    static create(name:string,age:number):Person{
        return new Person(name,age)
    }
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody

class Student extends Person{
    constructor(name:string,age:number){
        super(name,age)
        console.log(this.name)
    }
}
const tom = new Student('tom',18)
tom.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody

  • TypeScript 类 readonly 只读属性
class Person {
    public name:string
    private readonly age:number // readonly 一般跟在修饰符后面
    protected constructor (name:string,age:number){
        this.name=name
        this.age=age
    }
    sayHi(msg:string):void{
        //this.age=20 // 修改只读属性报错   Cannot assign to 'age' because it is a read-only property.
        console.log(`${this.name} age is ${this.age}`)
        console.log(`${this.name} say ${msg}`)
    }
    static create(name:string,age:number):Person{
        return new Person(name,age)
    }
}
const person = Person.create('tom',18)
person.sayHi('hello everybody')
// tom age is 18
// tom say hello everybody
  • TypeScript 类与接口

通过接口定义类的成员

interface EatAndRun {
    eat(food: string): void
    walk(step: number): void
}

class Person implements EatAndRun{
    eat(food: string): void {
        console.log(`优雅进餐${food}`)
    }
    walk(step: number): void {
        console.log(`直立行走${step}`)
    }
}
class Animal implements EatAndRun{
    eat(food: string): void {
        console.log(`粗鲁进食${food}`)
    }
    walk(step: number): void {
        console.log(`爬行${step}`)
    }
}

接口最好的使用或者定义方式:一个接口对应一个能力

interface Eat {
    eat(food: string): void
}
interface walk {
    walk(step: number): void
}
class Person implements Eat,walk{
    eat(food: string): void {
        console.log(`优雅进餐${food}`)
    }
    walk(step: number): void {
        console.log(`直立行走${step}`)
    }
}
class Animal implements Eat,walk{
    eat(food: string): void {
        console.log(`粗鲁进食${food}`)
    }
    walk(step: number): void {
        console.log(`爬行${step}`)
    }
}
  • TypeScript 类抽象类
abstract class Animal{
    eat(food: string): void {
        console.log(`粗鲁进食${food}`)
    }
    // 定义行走抽象方法
    abstract  walk (step: number): void
}
class Dog extends Animal{
    // 实现行走放大
    walk(step: number): void {
        console.log(`爬行${step}`)
    }
}

const dog = new Dog
console.log(dog.eat('骨头')) // 粗鲁进食骨头
console.log(dog.walk(10)) // 爬行10
  • TypeScript 泛型

定义函数接口或者类的时候不能明确的类型,在使用的时候在指定具体类型

// 只能实现 返回 number 格式的数组
function createNumberArray(len:number,value:number):number[]{
    const arr=Array<number>(len).fill(value)
    return arr
}
console.log(createNumberArray(3,100)) // [ 100, 100, 100 ]

// 不明确的类型使用 T 来表示  T 会在函数调用时传入
function createArray<T>(len:number,value:T):T[]{
    const arr=Array<T>(len).fill(value)
    return arr
}
console.log(createArray<number>(3,100)) // [ 100, 100, 100 ]
console.log(createArray<string>(3,'abc')) // [ 'abc', 'abc', 'abc' ]

你可能感兴趣的:(JavaScript)