typescript学习回顾(三)

今天继续来分享ts的相关概念,枚举,ts模块化,接口和类型兼容性

ts的扩展类型:类型别名,枚举,接口和类

枚举

基础概念

枚举通常用于约束某个变量的取值范围。当然字面量和联合类型配合使用,也可以达到同样的目标。

为什么使用枚举

但是使用字面量和联合联系会存在一个问题

  • 逻辑含义和真实值容易混淆,修改真实值的时候,会产生大量的修改
  • 字面量类型不会进入编译结果

使用枚举的话就不会出现这种问题

如何定义枚举
/*
enum 枚举名{
    枚举字段1 = 值1,
    枚举字段2 = 值2,
    ..
}
*/

//实例
 enum Gender {
     Male = "帅哥",
    Female = "美女"
 }

// 先生 女士  男 女 male  female
 let gender: Gender;

 gender = Gender.Male;
 gender = Gender.Female;

编译后的js代码

var Gender;
(function (Gender) {
    Gender["Male"] = "\u5E05\u54E5";
    Gender["Female"] = "\u7F8E\u5973";
})(Gender || (Gender = {}));
// 先生 女士  男 女 male  female
let gender;
gender = Gender.Male;
gender = Gender.Female;

下面给个使用联合类型的例子

type Gender = "男" | "女";
let g:Gender;

g = '女';
g = '男';

编译后的js代码

let g;
g = '女';
g = '男';

可以非常清晰的看出枚举的好处,会把枚举里的属性也编译到结果里面,避免了代码里面大量的真实值。

枚举的规则
  • 枚举的字段值可以是字符串或数字
  • 数字枚举的值会自动自增
enum Level {
    level1 : 1,
    level2,
    level3
}

let l: Level = Level.level1;
console.log(l)
l = Level.level2;
console.log(l)
l = Level.level3;
console.log(l)

控制台输出结果

1
2
3
  • 被数字枚举约束的变量,可以直接赋值为数字

  • 数字枚举的编译结果 和 字符串枚举有差异

小练习

这里使用枚举来做测试

//权限
enum Permission {
    Read = 1,   //0001
    Write = 2,  //0010
    Create = 4, //0100
    Delete = 8  //1000
}

//1.使用或运算组合权限
let p: Permission = Permission.Read | Permission.Write;//这样p就具备read和write权限

//2.判断是否拥有某个权限,使用&,且运算两边都是1才为1
function hasPermisson(target: Permission, per: Permission) {
    return (target & per) === per;
}

//3.如何删除某个权限
//通过异或运算,相同取0,不同取1
p = p ^ Permission.Write;

模块化

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

前端模块化标准:ES6、commonjs、amd、umd、system,esnext

TS中如何书写模块化语句

TS中,导入和导出模块,统一使用ES6的模块化标准

编译结果中的模块化

可配置

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

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

默认是用的es6语法

import fs from "fs";
import myModule from "./myModule";
如何在TS中书写commonjs模块化代码

如果想用commonjs语法的话需要在tsconfig.json里面配置

{
  "compilerOptions": { //编译选项
    "module": "CommonJS", //配置编译目标使用的模块化标准
  },
}

代码:

//导出
export = xxx

//导入
import xxx = require(xxx)

示例

//modules.ts
export = {
    name: 'kakarote',
    sum(a: number, b: number) {
        return a + b;
    }
}

//index.ts
import myModule = require('./myModule');
模块解析

关于ts的一个模块解析策略,它有两种模块解析策略

具体可以查看中文网:https://www.tslang.cn/docs/handbook/module-resolution.html,也可以去官网里查看

  • classic:经典模块解析策

    //一、对于相对路径引入的模块流程
    //root/src/folder/A.ts
    import { b } from "./moduleB"
    
    //这样引入的话首先它会查找
    //1. /root/src/folder/moduleB.ts
    //2. /root/src/folder/moduleB.d.ts
    
    //二、对于非相对路径的导入
    //编译器则会从包含导入文件的目录开始依次向上级目录遍历,尝试定位匹配的声明文件。
    //root/src/folder/A.ts
    import { b } from "moduleB"
    //它会这么查找
    //1./root/src/folder/moduleB.ts
    //2./root/src/folder/moduleB.d.ts
    //3./root/src/moduleB.ts
    //4./root/src/moduleB.d.ts
    //5./root/moduleB.ts
    //6./root/moduleB.d.ts
    //7./moduleB.ts
    //8./moduleB.d.ts
    
  • node:模块解析策略

    //一、对于相对路径引入的解析模块流程
    ///root/src/moduleA.js
    var x = require("./moduleB");
    
    //1.它会监测:/root/src/moduleB.js文件是否存在
    //2.监测/root/src/moduleB目录是否包含package.json模块,如果package.json指定了main模块,包含了{ "main": "lib/mainModule.js" },那么它就会引用/root/src/moduleB/lib/mainModule.js
    //3.监测/root/src/moduleB目录是否包含index.js,如果存在它就会被当做那个文件夹的main模块
    
    //二、非相对模块的解析流程
    ///root/src/moduleA.js
    var x = require("moduleB")
    
    //1./root/src/node_modules/moduleB.js
    //2./root/src/node_modules/moduleB/package.json (如果指定了"main"属性)
    //3./root/src/node_modules/moduleB/index.js
    
    //4./root/node_modules/moduleB.js
    //5./root/node_modules/moduleB/package.json (如果指定了"main"属性)
    //6.root/node_modules/moduleB/index.js
    
    //7./node_modules/moduleB.js
    //8./node_modules/moduleB/package.json (如果指定了"main"属性)
    //9./node_modules/moduleB/index.js
    
    注意Node.js在步骤(4)和(7)会向上跳一级目录。
    

接口

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

契约(标准)的形式

  • API文档,弱标准

  • 代码约束,强标准

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

接口约束对象
interface User {
    name: string
    age: number
    sayHello: () => void
}

//定义类型别名
// type User = {
//     name: string,
//     age: number,
//     sayHello: () => void
// }

let u: User = {
    name: "sdsad",
    age: 33,
    sayHello() {
        console.log('asdasdsasa')
    }
}

接口可以继承

class Banner extends React.Component{

}

可以通过接口之间的继承,实现多种接口的组合

使用类型别名可以实现类型的组合效果,需要通过&,它叫做交叉类型

它们的区别:

  • 子接口不能覆盖父接口的成员
  • 交叉类型会把相同成员的类型进行交叉

readonly

只读修饰符,修饰的目标是只读

只读修饰符不在编译结果中

类型兼容性

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

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

例子:

interface Duck {
    sound: "嘎嘎嘎",
    swim(): void
}

let person:Duck = {
    name: "伪装成鸭子的人",
    age: 1,
    sound: "嘎嘎嘎" as "嘎嘎嘎",
    swim() {
        console.log(this.name + '正在游泳,并发出了' + this.sound + "的声音")
    }
}

如果直接给person设置Duck类型约束会报错

在这里插入图片描述

但是可以使用下面这种方式

interface Duck {
    sound: "嘎嘎嘎",
    swim(): void
}

let person = {
    name: "伪装成鸭子的人",
    age: 1,
    sound: "嘎嘎嘎" as "嘎嘎嘎",
    swim() {
        console.log(this.name + '正在游泳,并发出了' + this.sound + "的声音")
    }
}


let duck: Duck = person;

如果直接用对象的字面量形式就会报错,但是赋值的形式它就会忽略,只要有sound和swim就不会报错

在这里插入图片描述

函数类型

我们可以看到arr的forEach的一个函数的ts定义

forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void

它这里要求这个回调函数的参数有这些,value,index,array,但是这里我们传递的时候可以少传递,但是不能多传递。

[34, 3].forEach(it => console.log(it));

Tips:

参数值:传递给目标函数参数可以少,不能多

返回值:如果函数要求返回一定要返回,不要求返回则可以随意

结语

今天的学习回顾就到这里了!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值