当前位置:主页 > 网页前端 > JavaScript代码 >

Typescript类型系统FLOW静态检查基本规范

时间:2022-07-21 11:09:42 | 栏目:JavaScript代码 | 点击:

Typescript是一门基于JavaScript之上的语言,重点解决了JavaScript自由类型系统的问题。

使用Typescript可以大大提高代码的可靠程度。

类型系统

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

强类型:语言层面限制函数的实参类型必须与形参类型相同

弱类型:语言层面不会限制实参的类型

由于这种强弱类型之分根本不是某一个权威机构的定义,所以导致后人对这种鉴定方式的细节,出现了不一致的理解。但整体上大家的鉴定方式都是在描述强类型有更强的类型约束,而弱类型中几乎没有什么约束。

强类型语言中不允许有任意的隐式类型转换,而弱类型语言中则允许任意的数据隐式类型转换。

强类型不允许随意的隐式类型转换,而弱类型则是允许的。

变量类型允许随时改变的特点,不是强弱类型的差异。

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

JavaScript就是动态型语言,而且变量的类型随时可以改变。

常用编程语言:

JavaScript自由类型系统的问题

JavaScript是弱类型且动态类型,甚至可以说它没有类型。它的特征就是[任性]、[不靠谱],缺失了类型系统的可靠性。

JavaScript没有编译环节

大规模应用下,弱类型/动态类型这种优势就变成了短板。

弱类型的问题:君子约定有隐患,强制要求有保障。

强类型的优势:

Flow静态类型检查方案

//@flow
function sum(a: number, b: number) {
    return a + b
}
console.log(sum(10, 20));
console.log(sum('10', '20'));

Flow只是一个小工具。

安装:flow-bin

通过编译除注解

flow开发者工具:flow language support

类型注解

function add(a:number,b:number){
	return a+b
}
let num:number = 90
function foo():void{}

flow原始类型

const a: string = 'foobar'
const b: number = Infinity // NaN // 100
const c: boolean = false // true
const d: null = null
const e: void = undefined
const f: symbol = Symbol()

flow数组类型

const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
// 元组
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 } = {}
obj3.key1 = 'value1'
obj3.key2 = 'value2'

flow函数类型

 //@flow
function foo (callback: (string, number) => void) {
  callback('string', 100)
}
foo(function (str, n) {
  // str => string
  // n => number
})

flow特殊类型

// 字面量类型
const a: 'foo' = 'foo'
const type: 'success' | 'warning' | 'danger' = 'success'
// ------------------------
// 声明类型
type StringOrNumber = string | number
const b: StringOrNumber = 'string' // 100
// ------------------------
// Maybe 类型
const gender: ?number = undefined

任意类型 Mixedany

//@flow
// string | number | boolean | ....
function passMixed (value: mixed) {
  if (typeof value === 'string') {
    value.substr(1)
  }
  if (typeof value === 'number') {
    value * value
  }
}
passMixed('string')
passMixed(100)
// ---------------------------------
function passAny (value: any) {
  value.substr(1)
  value * value
}
passAny('string')
passAny(100)

Typescript语言规范与基本应用

任何一种JavaScript运行环境都支持。功能更为强大,生态也更健全、更完善。

Typescript是JavaScript的超集。

微软自研的开发工具对Typescript支持特别好。

Typescript(渐进式)–前端领域中的第二语言。

Typescript缺点:

使用Typescript之前要先安装依赖。

标准库就是内置对象所对应的声明。

显示中文错误信息:yarn tsc --locale zh-CN,vs-code也可以设置locale为中文

Typescript作用域

我们可以用立即执行函数/export导出模块来创建一个单独作用域。

//立即执行函数
(function () {
    const a = 9
})()
//模块导出
export aa = {
    a: 23
}

Typescript原始类型

//原始数据类型
const a: string = 'foo'
const b: number = 100  //NaN/Infinity
const c: boolean = true  //false
const d: boolean = null
const e: void = undefined
const f: null = null
const g: undefined = undefined
const h: symbol = Symbol()

Typescript Object类型

并不单指普通的对象类型,而是泛指所有的非原始类型:对象、数组、函数。

//Object类型
const aa: object = function () { }  //[]//{}
const obj: { foo: number, bar: string } = { foo: 123, bar: 'aaa' }

更专业的方式是使用接口。

Typescript数组类型

有两种定义方式:

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

Typescript元组类型(turple)

是一种特殊的数据结构,其实元组就是一个明确元素数量以及每个元素类型的数组,各个元素的类型不必要完全相同。定义方式:字面量方式

//元组类型
//元组(tuple)
export { }  //确保跟其他示例没有成员冲突
const tuple: [number, string] = [10, 'rock']
console.log(tuple[0]);  //10
console.log(tuple[1]);  //rock
//解构赋值
const [num, age] = tuple
Object.entries({
    foo: 123,
    zar: 432
})

Typescript枚举类型(enum)

enum Status {
    Draft = 'aaa',
    Unpulished = 'bbb',
    Published = 'ccc'
}

如果确认代码中不会使用索引器去访问枚举,就可以使用常量枚举。

//常量枚举
const post = {
    title: 'Hello',
    content: 'TypeScript',
    status: 'ok'
}

枚举类型会入侵到我们运行时的代码,它会影响到我们编译后的结果。我们在TypeScript中使用的大多数类型,它在经过编译转换后,最终都会被移除掉,因为它只是为了我们在编译过程中做类型检查,但枚举类型不会,它最终会变为一个双向键值对对象。

TypeScript函数类型

JavaScript中又两种函数定义方式:

函数声明

//函数声明方式
// function func1(a: number, b?: number): string {
// function func1(a: number, b: number=90): string {
function func1(a: number, ...rest: number[]): string {
    return 'hello'
}
func1(12, 34)
func1(30)

使用参数可选、参数默认值、剩余参数都需要放在参数列表的最后一个参数位置。

函数表达式

//函数表达式
const func2 = (a: number, b: number) => string = function (a: number, b: number): string {
    return 'func2'
}

TypeScript任意类型

因为any是弱类型,也是动态类型,所以TypeScript不会对any做类型检查。所以它存在类型安全问题,我们不要轻易去使用它。

function stringify(value: any) {
    return JSON.stringify(value)
}
stringify('string')
stringify(123)
stringify(true)
let foo: any = 'string'
foo = 100
foo.bar()

隐式类型判断

如果我们没有通过类型注解去标注一个变量,TypeScript会根据这个变量的使用情况去推断这个变量的类型。

//隐式类型推断
let age = 10 //number
// age = 'aaa'
let foo;
foo = 45
foo = 'aaa'

虽然定义变量时如果不给它指定类型,也不给初始值,TypeScript会自动帮他注解为any类型,但还是建议定义时就注明类型。

TypeScript类型断言

在某些特殊情况下,TypeScript无法去推断一个变量的类型,而我们作为开发者,我们根据代码的使用情况,我们是会明确知道这个变量到底是什么类型的。类型断言的方式:

const nums = [12, 34, 56]
const res = nums.find(i => i > 10)
//断言方式一--as关键字
const num1 = res as number
//断言方式二---泛型
const num2 = <number>res

类型断言并不是类型转换,类型转换是代码在运行时的概念,而类型断言是代码在编译时的概念。当代码编译过后,断言也就不存在了。

TypeScript接口(Interface)

一种规范或者一种契约。它可以用来去约定对象的结构。去使用一个接口,就必须去遵守它所有的约定。

interface Post {
    title: string
    content: string
}
function printPost(post: Post) {
    console.log(post.title);
    console.log(post.content);
}
printPost({
    title: 'hello',
    content: 'welcome'
})
//hello
//welcome

TypeScript中的接口只是为了我们有结构的数据,去做类型约束的,在实际运行阶段,它并没有任何意义。

interface Post {
    title: string
    subtitle?: string   //可选成员
    content: string
    readonly summary: string  //只读成员
}

TypeScript类(class)

类的特征:描述一类具体事物的抽象特征。

类可以用来描述一类具体对象的抽象成员。

ES6以前,JavaScript都是通过函数+原型模拟实现类。ES6开始,JavaScript中有了专门的class。

TypeScript增强了class的相关语法。

class Person {
    name: string
    age: number
    constructor(name: string, age: number) {
        this.name = name
        this.age = age
    }
    sayHi(msg: string): void {
        console.log((`I am ${this.name}, ${msg}`));
    }
}

类的访问修饰符(默认是public修饰符)

class Person {
  public name: string // = 'init name'
  private age: number
  protected gender: boolean
  constructor (name: string, age: number) {
    this.name = name
    this.age = age
    this.gender = true
  }
  sayHi (msg: string): void {
    console.log(`I am ${this.name}, ${msg}`)
    console.log(this.age)
  }
}
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)
  }
}

const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age)
// console.log(tom.gender)
const jack = Student.create('jack', 18)

类的只读属性readonly

protected readonly gender:boolean

类与接口

接口就是把共同的特征封装起来。

interface EatAndRun {
    eat(food: string): void
    run(distance: number): void
}
class Person implements EatAndRun {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}
class Animal implements EatAndRun {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}

一个接口只约束一个能力,一个类型去实现多个接口。

interface Eat {
    eat(food: string): void
}
interface Run {
    run(distance: number): void
}
class Person implements Eat, Run {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}
class Animal implements Eat, Run {
    eat(food: string): void {
        console.log(`美美的吃:${food}`);
    }
    run(distance: number) {
        console.log(`直立行走:${distance}`);
    }
}

TypeScript抽象类

抽象类与接口有点类似,也可以用来去约束子类中必须拥有某个成员。

但是抽象类可以包含一些具体的实现,但是接口只是成员的抽象,不包含具体的实现。

抽象类不能用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(`四肢爬行:${distance}`);
    }
}
const d = new Dog()
d.eat('肉')
d.run(200)

TypeScript泛型

泛型:我们在定义函数、接口或类时,没有指定具体的类型,等到使用时再去指定具体类型的特征。

function CreateNumberArray(length: number, value: number): number[] {
    const arr = Array<number>(length).fill(value)
    return arr
}
function CreateStringArray(length: number, value: string): string[] {
    const arr = Array<string>(length).fill(value)
    return arr
}
function CreateArray<T>(length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}
const res = CreateArray<string>(3, 'foo')

TypeScript类型声明

一个成员在定义时没有声明类型,在使用时单独为它做出明确声明。

import { camelCase } from 'lodash'
import qs from 'query-string'
qs.parse('?key=value&key2=value2')
declare function camelCase(input: string): string
const res = camelCase('hello')

您可能感兴趣的文章:

相关文章