TypeScript(二)

241 阅读9分钟

扩展类型-枚举

枚举

定义:枚举是扩展类型中的一种,枚举通常用于约束某个变量的取值范围。

字面量和联合类型配合使用,也可以达到相同的目的。

扩展类型:类型别名、枚举、接口、类

字面量类型的问题

  1. 在类型约束位置,会产生重复代码;
let gender: "男" | "女";
gender = "男";
gender = "女";

function user (g: "男" | "女) {};

可以使用类型别名解决问题

type Gender = "男" | "女" ; // 将 “男” | “女” 抽离成类型别名,避免代码重复
let gender: Gender;
gender = "男";
gender = "女";

function user (g: Gender) {};
  1. 逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改。(无法用类型别名解决);
  2. 字面量类型不会进入到编译结果;

枚举可以完美的解决以上的问题。

如何定义一个枚举

enum 枚举名{
  枚举字段1 = 值1,
  枚举字段2 = 值2,
  ...
}

enum Gender{
  male = "男", // male是逻辑名称,“男”是真实的值,也可能是先生,帅哥
  female = "女", // female是逻辑名称,“女”是真实的值,也可能是女士、美女
}

let gender: Gender;
gender = Gender.male; // 赋值时赋的是逻辑名称
gender = Gender.female;

枚举会参与编译,会在编译结果中出现,在编译结果中表现为对象

var Gender;
(function (Gender) {
    Gender["male"] = "\u7537";
    Gender["female"] = "\u5973";
})(Gender || (Gender = {}));
let gender;
gender = Gender.male;
gender = Gender.female;

枚举的规则

  1. 枚举的字段值可能是字符串或数字
  2. 数字枚举的值会自动递增
  3. 被数字枚举约束的变量,可以直接赋值为数字

企业开发经验

  1. 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
  2. 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值

demo:使用枚举优化扑克牌

 
// 枚举改写扑克牌
type Deck = Card[];
type Card = {
  color: Color,
  mark: Mark,
}
  
enum Color {
  heart = "♥",
  spade = "♠",
  club = "♣",
  diamond = "♦",
}
enum Mark {
  A = "A",
  two = "2",
  three = "3",
  four = "4",
  five = "5",
  six = "6",
  seven = "7",
  eight = "8",
  nine = "9",
  ten = "10",
  eleven = "J",
  twelve = "Q",
  thirteen = "K",
}

function createDeck (): Deck {
  const deck : Deck = [];

  const marks = Object.values(Mark);
  const colors = Object.values(Color);

  for (const m of marks) {
      for (const c of colors ) {
        deck.push({
          color: c,
          mark: m
        })
      }
  }

  return deck;
}

function printDeck(deck: Deck) {
  let result = '\r';
  deck.forEach((card, i) => {
    let str = card.color + card.mark;
    result += str + '\t';
    if ((i+1)%6 === 0) {
      result += '\n';
    }
  })
  console.log(result);
}

const deck = createDeck();
printDeck(deck);

打印出来的结果
结果
结果

扩展知识-枚举的位运算

一个文件有4种权限,读、写、删、创建

写成枚举

enum Permission {
  Read = 1, // 0001
  Write = 2, // 0010
  Create = 4, // 0100
  Delete = 8 // 1000
}
  1. 如何组合权限 或运算:参加运算的两个数据,按二进制位进行“与”运算,有一位是1就得1;
let p: Permission = Permission.Read | Permission.Write;
  1. 如何判断是否拥有某个权限 与运算:参加运算的两个数据,按二进制位进行“与”运算,全部为1才得1;
function hasPermission(target: Permission, per: Permission): boolean {
  return (target & per) === per;
}
// 例:判断p是否有可读的权限
hasPermission(p, Permission.Read);
  1. 如何删除某个权限 // 异或运算:参加运算的两个数据,按二进制位进行“与”运算,相同取零,不同取一;
p = p ^ Permission.Write;

模块化

关于模块化的相关配置:

配置名称 含义
module 设置编译结果中使用的模块化标准
moduleResolution 设置解析模块的模式
noImplicitUseStrict 编译结果中不包含"use strict"
removeComments 编译结果移除注释
noEmitOnError 错误时不生成编译结果
esModuleInterop 启用es模块化交互非es模块导出

TS中如何书写模块化语句

最佳实践:TS中,导入和导出模块,统一使用ES6的模块化标准


不使用ES6模块化标准(不推荐)


# 编译结果中的模块化

可以在tsconfig.json文件配置

TS中的模块化在编译结果中:

- 如果编译结果的模块化标准是ES6: 没有区别
- 如果编译结果的模块化标准是commonjs:导出的声明会变成exports的属性,默认的导出会变成exportsdefault属性;

# 如何在TS中书写commonjs模块化代码

导出:export = xxx

导入:import xxx = require("xxx")

# 模块解析

模块解析:应该从什么位置寻找模块

TS中,有两种模块解析策略

- classic:经典
- node:node解析策略(唯一的变化,是将js替换为ts)
  - 相对路径```require("./xxx")```
  - 非相对模块```require("xxx")```

接口

接口:inteface

扩展类型:类型别名、枚举、接口、类

TypeScript的接口:用于约束类、对象、函数的契约(标准)

契约(标准)的形式:

  • API文档,弱标准
  • 代码约束,强标准

和类型别名一样,接口,不出现在编译结果中

  1. 接口约束对象

  2. 接口约束函数

类型兼容性

B->A,如果能完成赋值,则B和A类型兼容

鸭子辨型法(子结构辨型法):目标类型需要某一些特征,赋值的类型只要能满足该特征即可

  • 基本类型:完全匹配

  • 对象类型:鸭子辨型法

类型断言

当直接使用对象字面量赋值的时候,会进行更加严格的判断

  • 函数类型

参数:传递给目标函数的参数可以少,但不可以多

返回值:要求返回必须返回;不要求返回,你随意;

TS中的类

面向对象思想

基础部分,学习类的时候,仅讨论新增的语法部分。

属性

使用属性列表来描述类中的属性

属性的初始化检查

strictPropertyInitialization:true

属性的初始化位置:

  1. 构造函数中
  2. 属性默认值

属性可以修饰为可选的

属性可以修饰为只读的

使用访问修饰符

访问修饰符可以控制类中的某个成员的访问权限

  • public:默认的访问修饰符,公开的,所有的代码均可访问
  • private:私有的,只有在类中可以访问
  • protected:暂时不讲

Symble

属性简写

如果某个属性,通过构造函数的参数传递,并且不做任何处理的赋值给该属性。可以进行简写

访问器

作用:用于控制属性的读取和赋值

泛型

有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该保持一致或有关联的信息)

泛型:是指附属于函数、类、接口、类型别名之上的类型

泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以用该变量来代替,只有到调用时,才能确定它的类型

很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型

如果无法完成推导,并且又没有传递具体的类型,默认为空对象

泛型可以设置默认值

在函数中使用泛型

在函数名之后写上<泛型名称>

如何在类型别名、接口、类中使用泛型

直接在名称后写上<泛型名称>

泛型约束

泛型约束,用于现实泛型的取值

多泛型

深入理解类和接口

面向对象概述

为什么要讲面向对象

  1. TS为前端面向对象开发带来了契机

JS语言没有类型检查,如果使用面向对象的方式开发,会产生大量的接口,而大量的接口会导致调用复杂度剧增,这种复杂度必须通过严格的类型检查来避免错误,尽管可以使用注释或文档或记忆力,但是它们没有强约束力。

TS带来了完整的类型系统,因此开发复杂程序时,无论接口数量有多少,都可以获得完整的类型检查,并且这种检查是据有强约束力的。

  1. 面向对象中有许多非常成熟的模式,能处理复杂问题

在过去的很多年中,在大型应用或复杂领域,面向对象已经积累了非常多的经验。

什么是面向对象

面向对象:Oriented(基于) Object(事物),简称OO。

是一种编程思想,它提出一切以类对切入点思考问题。

其他编程思想:面向过程、函数式编程

学开发最重要最难的是什么?思维

面向过程:以功能流程为思考切入点,不太适合大型应用

函数式编程:以数学运算为思考切入点

面向对象:以划分类为思考切入点。类是最小的功能单元

类:可以产生对象的模板。

如何学习

  1. TS中的OOP (面向对象编程,Oriented Object Programing)
  2. 小游戏练习

理解 -> 想法 -> 实践 -> 理解 -> ....