篇五:大前端基础之Flow 与 TypeScriipt笔记

1,080 阅读9分钟

文章输出主要来源:拉勾大前端高新训练营(链接)。小哥哥小姐姐请不要嫌弃啰嗦,下面肯定都是干货。

1 JavaScript语言类型系统

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

强类型:

  1. 语言层面限制函数的实参类型必须与形参类型相同
  2. 不允许任意的数据隐式类型转换

弱类型:

  1. 语言层面不会限制实参的类型
  2. 允许任意的数据隐式类型转换

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

**静态类型:**变量声明时候类型是明确的,声明之后类型不允许再修改

动态类型: 运行过程中变量类型才能被明确,而且变量的类型随时可以被改变

动态类型的变量是没有类型的但是变量中存的值是有类型的。

强类型的优势

  1. 错误可以更早暴露
  2. 代码更智能,编码更准确
  3. 重构更牢靠
  4. 减少不必要的类型判断

2 Flow

通过类型注解对变量做标注。

2.1 基础使用

  • 安装flow: yarn add flow-bin -D

  • js中引入flow的声明:

    // @flow
    
  • 为js变量添加类型注释

    function add (a: number, b: number) {
      return a + b;
    }
    
    add(100, 100);
    

add('100', '100');


- 使用flow命令进行检查`yarn flow` or `npx flow`



### 2.2 编译移除flow类型注释

js代码中变量添加的类型注释不是js的标准语法,只能在开发阶段使用,还需进行编译移除类型注释才能正确运行js代码。

**1. flow官方移除工具:** `yarn add flow-remove-types -D`

使用方法: `yarn flow-remove-types source -d target`, 例: `yarn flow-remove-types ./flow -d dist`

**2. 使用babel进行移除:**`yarn add @babel/core @babel/cli @babel/preset-flow -D`

添加`.babelrc`配置文件

```json
{
"presets": ["@babel/preset-flow"]
}

使用命令与flow官方工具一致:yarn babel ./flow -d ./dist

2.3 flow类型检查编辑器支持

vscode插件Flow Language Support保存代码后才会进行检查,体验会慢

2.4 类型注解

变量标注

let num: number = 100

标注函数类型

function foo():string {
  return 'hello'
}
// 无返回值,标注void
function bar():void {
  console.log('hello world');
}

2.5 类型

1. flow原始类型

对应于javascript中的原始类型

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

2. 数组类型

通过Array<T>泛型标注进行表示,或T[]进行表示

const arr1: Array<string> = ['a', 'b'];
const arr2: number[] = [1,2,3];

固定长度的数组/元祖

const foo: [string, number] = ['a', 1];

3. 对象类型

通过对象所有属性声明类型为对象声明类型,?为可选项

const obj1: {
  foo: string,
  bar: number,
  baz?: boolean
} = {
  foo: 'hello',
  bar: 123
}

通过添加索引签名限制对象属性类型

const obj2: {[string]: string} = {}

4. 函数类型

通过类似箭头函数的索引签名定义函数类型

function fn1(callback: (string, number) => void) {
  callback('string', 100);
}

5. 特殊类型

字面量类型:字面量类型通常配合|联合类型(或类型)一起使用

let a: 'foo' | 'bar' | 'baz';
a = 'foo'

通过type定义新的类型

type StringOrNumber = string | number;
type Person = {
  name: string;
  age: number;
}

const tom: Person = {
  name: 'tom',
  age: 18
}

maybe类型?

通过?定义可选的变量,相当在具体类型上扩展了nullundefined的值

const name: ?string = undefined;
const name1: ?string = null;
const name2: ?string = 'tom';

6. mixed与any

mixedany都可以接收任意类型

mixed是所有类型的联合类型

function fn(value: mixed) {
  console.log(value);
}

fn(100);
fn('hello');
fn({name: 'tom'});

any也可以接收任意类型

function fn1(value: any) {
  console.log(value);
}

fn1(100);
fn1('hello');
fn1({name: 'tom'});

mixedany的区别:

mixed是强类型,声明为mixed类型的变量不能随便调用例如字符串的方法str.substr();

any是弱类型,可以任意调用其他类型的方法,在语法层面不报错

2.6 运行环境api

在不同环境flow提供了不同环境的类型定义文件

3 TypeScript

3.1 安装与简单使用

安装:yarn add typescript -D

const hello = (name: string) => {
  console.log(`hello ${name}`);
}

hello('tom');

编译:tsc filename.ts

编译完成生成js后可用node直接运行。

配置文件:yarn tsc --init or npx tsc --init生成tsconfig.json配置文件

使用tsc命令运行某个文件配置文件不生效,直接使用tsc命令编译整个项目会生效

3.2 Js 6种原始类型在ts中的应用

基本与flow一致

注意:

  • 严格模式下string, number, boolean不可为空null
  • 非严格模式下string, number, boolean可以为空null
  • 严格模式下只能为undefined,非严格模式下可以为空null
  • 如果ts配置文件target为es5,则Symbol类型报错(使用任何ES6+新定义的内容都会报错),因为默认引用的是lib.es5.d.ts
    • 解决方案1:将target改为es2015
    • 解决方案2: 在lib配置数组中添加ES2015,此时原本的库被覆盖console.log报错,还需引入DOM库,TS中将DOM跟BOM API都定义在了DOM库中
const a: string = 'hello';
const b: number = 100; // NaN; // Infinity
const c: boolean = true; // false;

// 严格模式下string, number, boolean不可为空null
// const aa: string = null;
// const bb: number = null; // NaN; // Infinity
// const cc: boolean = null; // false;
// 非严格模式下string, number, boolean可以为空null

// 严格模式下只能为undefined,非严格模式下可以为空null
const e: void = undefined;

const f: null = null;
const g: undefined = undefined;

const h: symbol = Symbol() 

3.3 TS 中的object类型

ts中的object类型不单指普通对象,它代表的是所有非原始类型,例如:对象数组函数

普通对象通过类似对象字面量的方式进行类型声明

const obj: {
  name: string;
  age: number;
} = { 
  name: 'tom',
  age: 18,
}

3.4 数组类型

与flow中的定义方式基本一致。

  • 通过泛型接口定义: const arr1: Array<number> = []
  • 通过类型+[]定义: const arr2: number[] = []

3.5 元组类型

通过类似数组字面量方式定义元组,元组本质也是个数组

const tuple: [number, string] = [1, 'tom'];

3.6 枚举类型

enum PostStatus {
  Draft = 0, // 枚举值默认从0开始,可以自定义开始的值,后续会自增
  Unpublished = 1,
  Published = 2
}

枚举的结果会被编译为双向键值对

var PostStatus;
(function (PostStatus) {
    PostStatus[PostStatus["Draft"] = 0] = "Draft";
    PostStatus[PostStatus["Unpublished"] = 1] = "Unpublished";
    PostStatus[PostStatus["Published"] = 2] = "Published";
})(PostStatus || (PostStatus = {}));

如果枚举值不为数字,不会被编译为双向键值对

// 定义
enum PostStatus2 {
  Draft = '0',
  Unpublished = '1',
  Published = '2'
}
// 编译后
var PostStatus2;
(function (PostStatus2) {
    PostStatus2["Draft"] = "0";
    PostStatus2["Unpublished"] = "1";
    PostStatus2["Published"] = "2";
})(PostStatus2 || (PostStatus2 = {}));

常量枚举会被编译为普通对象,不能通过枚举值查找key,定义方式为

const enum PostStatus {
  Draft,
  Unpublished,
  Published
}

3.7 函数类型

通过函数声明方式定义函数时的函数类型声明

function func1(a: number, b: string): string {
  return 'hello world';
}

通过?可以声明可选参数,也可为参数赋默认值,这两种方式都要放在参数列表末尾

箭头函数形式的函数类型声明:

const func: (a: number, b: string) => string = function(a, b) {
  return 'hello world'
}

3.8 任意类型

any可以表示为任意类型,使用any类型可以接收所有的类型,并且可以调用其上的所有方法,在语法上不会报错。

3.9 隐式类型推断

ts可以进行隐式类型推断

let age = 18 // 隐式推断为number
age = "18" // 报错

3.10 类型断言

通过as<>两种形式都可以进行类型断言,在JSX中<>会与标签冲突,无法使用。

在开发者比ts更明确知道变量类型的时候,可以使用类型断言。

const arr: number[] = [1,2,3,4];
const res = arr.find(item => item === 1) as number;

3.11 接口Interface

通过interface关键词可以声明接口类型,可以利用接口声明自定义的数据结构

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

function printPost (post: Post) {
  console.log(post.title);
  console.log(post.content);
}

动态成员

interface Cache {
  [prop: string]: string //键值都是string
}

3.12 类class

ts中的类对ES6的类做了增强

基础使用

class Person {
  name: string; // 类的成员必须先声明
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  sayHello(friend: string) {
    console.log('hello ', friend);
  }
}

访问修饰符

类的所有成员都可以添加访问修饰符:public(默认)private:私有,protected:受保护,子类可以访问,实例不能直接访问

class Person {
  public name: string;
  private age: number;
  protected gender: boolean;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
    this.gender = true;
  }

  sayHello(friend: string) {
    console.log('hello ', friend);
  }
}

class Student extends Person{
  constructor(name: string, age: number) {
    super(name, age);
    console.log(this.gender);
  }
}

const tom = new Person('tom', 18);
console.log(tom.name); // tom
// console.log(tom.age); // 报错
// console.log(tom.gender); // 报错

constructor默认为public,如果被标记为private,就不能再外部通过new的方式实例化对象,也无法被继承,可以通过静态方法,在静态方法中new出新的实例并返回。

constructor如果被标记为protected,也不能再外部new出新实例,但是可以继承。

只读属性

通过readonly关键词可以将类属性设置为只读属性。需要在声明时进行初始化,或者在构造函数中进行初始化,一旦初始化,就不能再更改。

类与接口

类可以通过implements实现(多个)接口

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):void {
    console.log('直立行走:', distance);
  }
}

class Animal implements Eat, Run {
  eat(food: string): void {
    console.log('动物吃东西:', food);
  }
  run(distance: number):void {
    console.log('爬行:', distance);
  }
}

抽象类

通过添加abstract关键词可以使一个类变为抽象类,抽象类只能被继承,不能被实例化。抽象类中也可以定义抽象方法,也需要用abstract修饰。

抽象类可以包含某些方法的实现,接口只有成员的抽象,没有实现。

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 dog = new Dog();
dog.eat('狗粮');
dog.run(100);

3.13 泛型

泛型可以用来定义动态的类型,通过传入类型确认最终的类型。

function createArray<T> (length: number, value: T): Array<T> {
  const arr = Array<T>(length).fill(value);
  return arr;
}

const res1: number[] = createArray<number>(3, 100);
const res2: string[] = createArray<string>(3, 'hello');

3.14 类型声明

如果使用一个没有类型模块@types/xxx的第三方模块,需要自己去写类型声明,可以使用declare进行类型声明。